--- /dev/null
+{
+ "libs": [
+ ],
+ "plugins": {
+ "node": {}
+ }
+}
\ No newline at end of file
### browser
-* Default: OS X: `"open"`, others: `"google-chrome"`
+* Default: OS X: `"open"`, Windows: `"start"`, Others: `"xdg-open"`
* Type: String
The browser that is called by the `npm bugs` command to open websites.
### browser
-* Default: OS X: `"open"`, others: `"google-chrome"`
+* Default: OS X: `"open"`, Windows: `"start"`, Others: `"xdg-open"`
* Type: String
The browser that is called by the `npm docs` command to open websites.
### browser
-* Default: OS X: `"open"`, others: `"google-chrome"`
+* Default: OS X: `"open"`, Windows: `"start"`, Others: `"xdg-open"`
* Type: String
The browser that is called by the `npm docs` command to open websites.
## How do I install node with npm?
-You don't. Try one of these:
+You don't. Try one of these node version managers:
-* <https://github.com/isaacs/nave>
-* <https://github.com/visionmedia/n>
-* <https://github.com/creationix/nvm>
+Unix:
+
+* <http://github.com/isaacs/nave>
+* <http://github.com/visionmedia/n>
+* <http://github.com/creationix/nvm>
+
+Windows:
+
+* <http://github.com/marcelklehr/nodist>
+* <https://github.com/hakobera/nvmw>
## How can I use npm for development?
+-- node_modules
+-- blerg (1.2.5) <---[A]
+-- bar (1.2.3) <---[B]
- | +-- node_modules
- | | `-- baz (2.0.2) <---[C]
- | | `-- node_modules
- | | `-- quux (3.2.0)
- | `-- asdf (2.3.4)
+ | `-- node_modules
+ | +-- baz (2.0.2) <---[C]
+ | | `-- node_modules
+ | | `-- quux (3.2.0)
+ | `-- asdf (2.3.4)
`-- baz (1.2.3) <---[D]
`-- node_modules
`-- quux (3.2.0) <---[E]
-Since foo depends directly on bar@1.2.3 and baz@1.2.3, those are
+Since foo depends directly on `bar@1.2.3` and `baz@1.2.3`, those are
installed in foo's `node_modules` folder.
Even though the latest copy of blerg is 1.3.7, foo has a specific
dependency on version 1.2.5. So, that gets installed at [A]. Since the
-parent installation of blerg satisfie's bar's dependency on blerg@1.x,
+parent installation of blerg satisfies bar's dependency on `blerg@1.x`,
it does not install another copy under [B].
Bar [B] also has dependencies on baz and asdf, so those are installed in
re-use the `baz@1.2.3` installed in the parent `node_modules` folder [D],
and must install its own copy [C].
-Underneath bar, the `baz->quux->bar` dependency creates a cycle.
-However, because `bar` is already in `quux`'s ancestry [B], it does not
+Underneath bar, the `baz -> quux -> bar` dependency creates a cycle.
+However, because bar is already in quux's ancestry [B], it does not
unpack another copy of bar into that folder.
-Underneath `foo->baz` [D], quux's [E] folder tree is empty, because its
+Underneath `foo -> baz` [D], quux's [E] folder tree is empty, because its
dependency on bar is satisfied by the parent folder copy installed at [B].
For a graphical breakdown of what is installed where, use `npm ls`.
<p>This function should not be used programmatically. Instead, just refer
to the <code>npm.bin</code> member.</p>
</div>
-<p id="footer">bin — npm@1.2.18</p>
+<p id="footer">bin — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This command will launch a browser, so this command may not be the most
friendly for programmatic use.</p>
</div>
-<p id="footer">bugs — npm@1.2.18</p>
+<p id="footer">bugs — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer">commands — npm@1.2.18</p>
+<p id="footer">commands — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/npm.html">npm(3)</a></li></ul>
</div>
-<p id="footer">config — npm@1.2.18</p>
+<p id="footer">config — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/publish.html">publish(3)</a></li><li><a href="../api/unpublish.html">unpublish(3)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">deprecate — npm@1.2.18</p>
+<p id="footer">deprecate — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This command will launch a browser, so this command may not be the most
friendly for programmatic use.</p>
</div>
-<p id="footer">docs — npm@1.2.18</p>
+<p id="footer">docs — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Since this command opens an editor in a new process, be careful about where
and how this is used.</p>
</div>
-<p id="footer">edit — npm@1.2.18</p>
+<p id="footer">edit — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The first element in the 'args' parameter must be a package name. After that is the optional command, which can be any number of strings. All of the strings will be combined into one, space-delimited command.</p>
</div>
-<p id="footer">explore — npm@1.2.18</p>
+<p id="footer">explore — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The silent parameter is not neccessary not used, but it may in the future.</p>
</div>
-<p id="footer">help-search — npm@1.2.18</p>
+<p id="footer">help-search — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p><a href="../doc/json.html">json(1)</a></p>
</div>
-<p id="footer">init — npm@1.2.18</p>
+<p id="footer">init — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when all packages have been
installed or when an error has been encountered.</p>
</div>
-<p id="footer">install — npm@1.2.18</p>
+<p id="footer">install — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Now, any changes to the redis package will be reflected in
the package in the current working directory</p>
</div>
-<p id="footer">link — npm@1.2.18</p>
+<p id="footer">link — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>For a list of all the available command-line configs, see <code>npm help config</code></p>
</div>
-<p id="footer">load — npm@1.2.18</p>
+<p id="footer">load — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
This means that if a submodule a same dependency as a parent module, then the
dependency will only be output once.</p>
</div>
-<p id="footer">ls — npm@1.2.18</p>
+<p id="footer">ls — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="VERSION">VERSION</h2>
-<p>1.2.18</p>
+<p>1.2.20</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<pre><code>var cmd = npm.deref("unp") // cmd === "unpublish"</code></pre>
</div>
-<p id="footer">npm — npm@1.2.18</p>
+<p id="footer">npm — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If the 'packages' parameter is left out, npm will check all packages.</p>
</div>
-<p id="footer">outdated — npm@1.2.18</p>
+<p id="footer">outdated — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/publish.html">publish(3)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">owner — npm@1.2.18</p>
+<p id="footer">owner — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If no arguments are supplied, then npm packs the current package folder.</p>
</div>
-<p id="footer">pack — npm@1.2.18</p>
+<p id="footer">pack — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically</p>
</div>
-<p id="footer">prefix — npm@1.2.18</p>
+<p id="footer">prefix — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Extraneous packages are packages that are not listed on the parent
package's dependencies list.</p>
</div>
-<p id="footer">prune — npm@1.2.18</p>
+<p id="footer">prune — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../api/owner.html">owner(3)</a></li></ul>
</div>
-<p id="footer">publish — npm@1.2.18</p>
+<p id="footer">publish — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>See <code>npm help build</code></p>
</div>
-<p id="footer">rebuild — npm@1.2.18</p>
+<p id="footer">rebuild — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/start.html">start(3)</a></li><li><a href="../api/stop.html">stop(3)</a></li></ul>
</div>
-<p id="footer">restart — npm@1.2.18</p>
+<p id="footer">restart — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically.</p>
</div>
-<p id="footer">root — npm@1.2.18</p>
+<p id="footer">root — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../api/test.html">test(3)</a></li><li><a href="../api/start.html">start(3)</a></li><li><a href="../api/restart.html">restart(3)</a></li><li><a href="../api/stop.html">stop(3)</a></li></ul>
</div>
-<p id="footer">run-script — npm@1.2.18</p>
+<p id="footer">run-script — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
and doesn't try to read your mind (it doesn't do any verb tense matching or the
like).</p>
</div>
-<p id="footer">search — npm@1.2.18</p>
+<p id="footer">search — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when the shrinkwrap has
been saved.</p>
</div>
-<p id="footer">shrinkwrap — npm@1.2.18</p>
+<p id="footer">shrinkwrap — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run tests on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">start — npm@1.2.18</p>
+<p id="footer">start — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run stop on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">stop — npm@1.2.18</p>
+<p id="footer">stop — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li>npm help json</li><li>git help submodule</li></ul>
</div>
-<p id="footer">submodule — npm@1.2.18</p>
+<p id="footer">submodule — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
used. For more information about how to set this config, check
<code>man 3 npm-config</code> for programmatic usage or <code>man npm-config</code> for cli usage.</p>
</div>
-<p id="footer">tag — npm@1.2.18</p>
+<p id="footer">tag — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run tests on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">test — npm@1.2.18</p>
+<p id="footer">test — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when all packages have been
uninstalled or when an error has been encountered.</p>
</div>
-<p id="footer">uninstall — npm@1.2.18</p>
+<p id="footer">uninstall — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If no version is specified, or if all versions are removed then
the root package entry is removed from the registry entirely.</p>
</div>
-<p id="footer">unpublish — npm@1.2.18</p>
+<p id="footer">unpublish — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The 'packages' argument is an array of packages to update. The 'callback' parameter will be called when done or when an error occurs.</p>
</div>
-<p id="footer">update — npm@1.2.18</p>
+<p id="footer">update — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
parameter. The difference, however, is this function will fail if it does
not have exactly one element. The only element should be a version number.</p>
</div>
-<p id="footer">version — npm@1.2.18</p>
+<p id="footer">version — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>corresponding to the list of fields selected.</p>
</div>
-<p id="footer">view — npm@1.2.18</p>
+<p id="footer">view — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically</p>
</div>
-<p id="footer">whoami — npm@1.2.18</p>
+<p id="footer">whoami — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer"><a href="../doc/README.html">README</a> — npm@1.2.18</p>
+<p id="footer"><a href="../doc/README.html">README</a> — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li></ul>
</div>
-<p id="footer">adduser — npm@1.2.18</p>
+<p id="footer">adduser — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prefix.html">prefix(1)</a></li><li><a href="../doc/root.html">root(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">bin — npm@1.2.18</p>
+<p id="footer">bin — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h3 id="browser">browser</h3>
-<ul><li>Default: OS X: <code>"open"</code>, others: <code>"google-chrome"</code></li><li>Type: String</li></ul>
+<ul><li>Default: OS X: <code>"open"</code>, Windows: <code>"start"</code>, Others: <code>"xdg-open"</code></li><li>Type: String</li></ul>
<p>The browser that is called by the <code>npm bugs</code> command to open websites.</p>
<ul><li><a href="../doc/docs.html">docs(1)</a></li><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">bugs — npm@1.2.18</p>
+<p id="footer">bugs — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">build — npm@1.2.18</p>
+<p id="footer">build — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">bundle — npm@1.2.18</p>
+<p id="footer">bundle — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li></ul>
</div>
-<p id="footer">cache — npm@1.2.18</p>
+<p id="footer">cache — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li></ul>
</div>
-<p id="footer">changelog — npm@1.2.18</p>
+<p id="footer">changelog — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">coding-style — npm@1.2.18</p>
+<p id="footer">coding-style — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">completion — npm@1.2.18</p>
+<p id="footer">completion — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h3 id="browser">browser</h3>
-<ul><li>Default: OS X: <code>"open"</code>, others: <code>"google-chrome"</code></li><li>Type: String</li></ul>
+<ul><li>Default: OS X: <code>"open"</code>, Windows: <code>"start"</code>, Others: <code>"xdg-open"</code></li><li>Type: String</li></ul>
<p>The browser that is called by the <code>npm docs</code> command to open websites.</p>
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">config — npm@1.2.18</p>
+<p id="footer">config — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/ls.html">ls(1)</a></li><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">dedupe — npm@1.2.18</p>
+<p id="footer">dedupe — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">deprecate — npm@1.2.18</p>
+<p id="footer">deprecate — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">developers — npm@1.2.18</p>
+<p id="footer">developers — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li></ul>
</div>
-<p id="footer">disputes — npm@1.2.18</p>
+<p id="footer">disputes — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h3 id="browser">browser</h3>
-<ul><li>Default: OS X: <code>"open"</code>, others: <code>"google-chrome"</code></li><li>Type: String</li></ul>
+<ul><li>Default: OS X: <code>"open"</code>, Windows: <code>"start"</code>, Others: <code>"xdg-open"</code></li><li>Type: String</li></ul>
<p>The browser that is called by the <code>npm docs</code> command to open websites.</p>
<ul><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">docs — npm@1.2.18</p>
+<p id="footer">docs — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/explore.html">explore(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">edit — npm@1.2.18</p>
+<p id="footer">edit — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/submodule.html">submodule(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/edit.html">edit(1)</a></li><li><a href="../doc/rebuild.html">rebuild(1)</a></li><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">explore — npm@1.2.18</p>
+<p id="footer">explore — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="How-do-I-install-node-with-npm">How do I install node with npm?</h2>
-<p>You don't. Try one of these:</p>
+<p>You don't. Try one of these node version managers:</p>
-<ul><li><a href="https://github.com/isaacs/nave">https://github.com/isaacs/nave</a></li><li><a href="https://github.com/visionmedia/n">https://github.com/visionmedia/n</a></li><li><a href="https://github.com/creationix/nvm">https://github.com/creationix/nvm</a></li></ul>
+<p>Unix:</p>
+
+<ul><li><a href="http://github.com/isaacs/nave">http://github.com/isaacs/nave</a></li><li><a href="http://github.com/visionmedia/n">http://github.com/visionmedia/n</a></li><li><a href="http://github.com/creationix/nvm">http://github.com/creationix/nvm</a></li></ul>
+
+<p>Windows:</p>
+
+<ul><li><a href="http://github.com/marcelklehr/nodist">http://github.com/marcelklehr/nodist</a></li><li><a href="https://github.com/hakobera/nvmw">https://github.com/hakobera/nvmw</a></li></ul>
<h2 id="How-can-I-use-npm-for-development">How can I use npm for development?</h2>
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li></ul>
</div>
-<p id="footer">faq — npm@1.2.18</p>
+<p id="footer">faq — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
+-- node_modules
+-- blerg (1.2.5) <---[A]
+-- bar (1.2.3) <---[B]
- | +-- node_modules
- | | `-- baz (2.0.2) <---[C]
- | | `-- node_modules
- | | `-- quux (3.2.0)
- | `-- asdf (2.3.4)
+ | `-- node_modules
+ | +-- baz (2.0.2) <---[C]
+ | | `-- node_modules
+ | | `-- quux (3.2.0)
+ | `-- asdf (2.3.4)
`-- baz (1.2.3) <---[D]
`-- node_modules
`-- quux (3.2.0) <---[E]</code></pre>
-<p>Since foo depends directly on bar@1.2.3 and baz@1.2.3, those are
+<p>Since foo depends directly on <code>bar@1.2.3</code> and <code>baz@1.2.3</code>, those are
installed in foo's <code>node_modules</code> folder.</p>
<p>Even though the latest copy of blerg is 1.3.7, foo has a specific
dependency on version 1.2.5. So, that gets installed at [A]. Since the
-parent installation of blerg satisfie's bar's dependency on blerg@1.x,
+parent installation of blerg satisfies bar's dependency on <code>blerg@1.x</code>,
it does not install another copy under [B].</p>
<p>Bar [B] also has dependencies on baz and asdf, so those are installed in
re-use the <code>baz@1.2.3</code> installed in the parent <code>node_modules</code> folder [D],
and must install its own copy [C].</p>
-<p>Underneath bar, the <code>baz->quux->bar</code> dependency creates a cycle.
-However, because <code>bar</code> is already in <code>quux</code>'s ancestry [B], it does not
+<p>Underneath bar, the <code>baz -> quux -> bar</code> dependency creates a cycle.
+However, because bar is already in quux's ancestry [B], it does not
unpack another copy of bar into that folder.</p>
-<p>Underneath <code>foo->baz</code> [D], quux's [E] folder tree is empty, because its
+<p>Underneath <code>foo -> baz</code> [D], quux's [E] folder tree is empty, because its
dependency on bar is satisfied by the parent folder copy installed at [B].</p>
<p>For a graphical breakdown of what is installed where, use <code>npm ls</code>.</p>
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li></ul>
</div>
-<p id="footer">folders — npm@1.2.18</p>
+<p id="footer">folders — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li></ul>
</div>
-<p id="footer">global — npm@1.2.18</p>
+<p id="footer">global — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/help.html">help(1)</a></li></ul>
</div>
-<p id="footer">help-search — npm@1.2.18</p>
+<p id="footer">help-search — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/README.html">README</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/help-search.html">help-search(1)</a></li><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer">help — npm@1.2.18</p>
+<p id="footer">help — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p> Display npm username</p>
</div>
-<p id="footer">index — npm@1.2.18</p>
+<p id="footer">index — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="https://github.com/isaacs/init-package-json">https://github.com/isaacs/init-package-json</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/version.html">version(1)</a></li></ul>
</div>
-<p id="footer">init — npm@1.2.18</p>
+<p id="footer">init — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/rebuild.html">rebuild(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/tag.html">tag(1)</a></li><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/shrinkwrap.html">shrinkwrap(1)</a></li></ul>
</div>
-<p id="footer">install — npm@1.2.18</p>
+<p id="footer">install — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/semver.html">semver(1)</a></li><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/version.html">version(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/rm.html">rm(1)</a></li></ul>
</div>
-<p id="footer">json — npm@1.2.18</p>
+<p id="footer">json — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">link — npm@1.2.18</p>
+<p id="footer">link — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
nested packages will <em>also</em> show the paths to the specified packages.
For example, running <code>npm ls promzard</code> in npm's source tree will show:</p>
-<pre><code>npm@1.2.18 /path/to/npm
+<pre><code>npm@1.2.20 /path/to/npm
└─┬ init-package-json@0.0.4
└── promzard@0.1.5</code></pre>
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/outdated.html">outdated(1)</a></li><li><a href="../doc/update.html">update(1)</a></li></ul>
</div>
-<p id="footer">ls — npm@1.2.18</p>
+<p id="footer">ls — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="VERSION">VERSION</h2>
-<p>1.2.18</p>
+<p>1.2.20</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<ul><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/README.html">README</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/index.html">index(1)</a></li><li><a href="../api/npm.html">npm(3)</a></li></ul>
</div>
-<p id="footer">npm — npm@1.2.18</p>
+<p id="footer">npm — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li></ul>
</div>
-<p id="footer">outdated — npm@1.2.18</p>
+<p id="footer">outdated — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/disputes.html">disputes(1)</a></li></ul>
</div>
-<p id="footer">owner — npm@1.2.18</p>
+<p id="footer">owner — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">pack — npm@1.2.18</p>
+<p id="footer">pack — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/root.html">root(1)</a></li><li><a href="../doc/bin.html">bin(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">prefix — npm@1.2.18</p>
+<p id="footer">prefix — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">prune — npm@1.2.18</p>
+<p id="footer">prune — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li><li><a href="../doc/deprecate.html">deprecate(1)</a></li><li><a href="../doc/tag.html">tag(1)</a></li></ul>
</div>
-<p id="footer">publish — npm@1.2.18</p>
+<p id="footer">publish — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">rebuild — npm@1.2.18</p>
+<p id="footer">rebuild — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/disputes.html">disputes(1)</a></li></ul>
</div>
-<p id="footer">registry — npm@1.2.18</p>
+<p id="footer">registry — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/README.html">README</a></li><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/prune.html">prune(1)</a></li></ul>
</div>
-<p id="footer">removing-npm — npm@1.2.18</p>
+<p id="footer">removing-npm — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">restart — npm@1.2.18</p>
+<p id="footer">restart — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">rm — npm@1.2.18</p>
+<p id="footer">rm — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prefix.html">prefix(1)</a></li><li><a href="../doc/bin.html">bin(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">root — npm@1.2.18</p>
+<p id="footer">root — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">run-script — npm@1.2.18</p>
+<p id="footer">run-script — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">scripts — npm@1.2.18</p>
+<p id="footer">scripts — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/view.html">view(1)</a></li></ul>
</div>
-<p id="footer">search — npm@1.2.18</p>
+<p id="footer">search — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">semver — npm@1.2.18</p>
+<p id="footer">semver — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">shrinkwrap — npm@1.2.18</p>
+<p id="footer">shrinkwrap — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">star — npm@1.2.18</p>
+<p id="footer">star — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/star.html">star(1)</a></li><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">stars — npm@1.2.18</p>
+<p id="footer">stars — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">start — npm@1.2.18</p>
+<p id="footer">start — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li></ul>
</div>
-<p id="footer">stop — npm@1.2.18</p>
+<p id="footer">stop — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/json.html">json(1)</a></li><li>git help submodule</li></ul>
</div>
-<p id="footer">submodule — npm@1.2.18</p>
+<p id="footer">submodule — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">tag — npm@1.2.18</p>
+<p id="footer">tag — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">test — npm@1.2.18</p>
+<p id="footer">test — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">uninstall — npm@1.2.18</p>
+<p id="footer">uninstall — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/deprecate.html">deprecate(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li></ul>
</div>
-<p id="footer">unpublish — npm@1.2.18</p>
+<p id="footer">unpublish — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/outdated.html">outdated(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">update — npm@1.2.18</p>
+<p id="footer">update — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/semver.html">semver(1)</a></li></ul>
</div>
-<p id="footer">version — npm@1.2.18</p>
+<p id="footer">version — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/search.html">search(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/docs.html">docs(1)</a></li></ul>
</div>
-<p id="footer">view — npm@1.2.18</p>
+<p id="footer">view — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">whoami — npm@1.2.18</p>
+<p id="footer">whoami — npm@1.2.20</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
, npm = require("./npm.js")
, registry = npm.registry
, read = require("read")
+ , userValidate = require("npm-user-validate")
, crypto
try {
}
function readUsername (c, u, cb) {
+ var v = userValidate.username
read({prompt: "Username: ", default: c.u}, function (er, un) {
if (er) {
return cb(er.message === "cancelled" ? er.message : er)
return readUsername(c, u, cb)
}
- if (un !== un.toLowerCase()) {
- log.warn('Username must be lowercase')
- return readUsername(c, u, cb)
- }
-
- if (un !== encodeURIComponent(un)) {
- log.warn('Username may not contain non-url-safe chars')
- return readUsername(c, u, cb)
- }
-
- if (un.charAt(0) === '.') {
- log.warn('Username may not start with "."')
+ var error = v(un)
+ if (error) {
+ log.warn(error.message)
return readUsername(c, u, cb)
}
}
function readPassword (c, u, cb) {
+ var v = userValidate.pw
+
if (!c.changed) {
u.p = c.p
return cb()
return readPassword(c, u, cb)
}
- if (pw.match(/['!:@"]/)) {
- log.warn('Sorry, passwords cannot contain these characters: \'!:@"')
+ var error = v(pw)
+ if (error) {
+ log.warn(error.message)
return readPassword(c, u, cb)
}
}
function readEmail (c, u, cb) {
+ var v = userValidate.email
+
read({prompt: "Email: ", default: c.e}, function (er, em) {
if (er) {
return cb(er.message === "cancelled" ? er.message : er)
return readEmail(c, u, cb)
}
- if (!em.match(/^.+@.+\..+$/)) {
- log.warn('Email must be an email address')
+ var error = v(em)
+ if (error) {
+ log.warn(error.message)
return readEmail(c, u, cb)
}
bugs.usage = "npm bugs <pkgname>"
-var exec = require("./utils/exec.js")
- , npm = require("./npm.js")
+var npm = require("./npm.js")
, registry = npm.registry
, log = require("npmlog")
, opener = require("opener")
log.verbose("linkStuff", [global, gnm, gtop, parent])
log.info("linkStuff", pkg._id)
- if (top && pkg.preferGlobal && !global) {
- log.warn("prefer global", pkg._id + " should be installed with -g")
- }
+ shouldWarn(pkg, folder, global, function() {
+ asyncMap( [linkBins, linkMans, !didRB && rebuildBundles]
+ , function (fn, cb) {
+ if (!fn) return cb()
+ log.verbose(fn.name, pkg._id)
+ fn(pkg, folder, parent, gtop, cb)
+ }, cb)
+ })
+}
- asyncMap( [linkBins, linkMans, !didRB && rebuildBundles]
- , function (fn, cb) {
- if (!fn) return cb()
- log.verbose(fn.name, pkg._id)
- fn(pkg, folder, parent, gtop, cb)
- }, cb)
+function shouldWarn(pkg, folder, global, cb) {
+ var parent = path.dirname(folder)
+ , top = parent === npm.dir
+ , cwd = process.cwd()
+
+ readJson(path.resolve(cwd, "package.json"), function(er, topPkg) {
+ if (er) return cb(er)
+
+ var linkedPkg = path.basename(cwd)
+ , currentPkg = path.basename(folder)
+
+ // current searched package is the linked package on first call
+ if (linkedPkg !== currentPkg) {
+
+ // don't generate a warning if it's listed in dependencies
+ if (Object.keys(topPkg.dependencies).indexOf(currentPkg) === -1) {
+
+ if (top && pkg.preferGlobal && !global) {
+ log.warn("prefer global", pkg._id + " should be installed with -g")
+ }
+ }
+ }
+
+ cb()
+ })
}
function rebuildBundles (pkg, folder, parent, gtop, cb) {
function linkMans (pkg, folder, parent, gtop, cb) {
if (!pkg.man || !gtop || process.platform === "win32") return cb()
+
var manRoot = path.resolve(npm.config.get("prefix"), "share", "man")
asyncMap(pkg.man, function (man, cb) {
if (typeof man !== "string") return cb()
, sxn = parseMan[2]
, gz = parseMan[3] || ""
, bn = path.basename(stem)
- , manSrc = path.join( folder, man )
, manDest = path.join( manRoot
, "man"+sxn
, (bn.indexOf(pkg.name) === 0 ? bn
: pkg.name + "-" + bn)
+ "." + sxn + gz
)
- linkIfExists(manSrc, manDest, gtop && folder, cb)
+
+ linkIfExists(man, manDest, gtop && folder, cb)
}, cb)
}
cache.unlock = unlock
var mkdir = require("mkdirp")
- , exec = require("./utils/exec.js")
, spawn = require("child_process").spawn
+ , exec = require("child_process").execFile
, once = require("once")
, fetch = require("./utils/fetch.js")
, npm = require("./npm.js")
, registry = npm.registry
, log = require("npmlog")
, path = require("path")
- , sha = require("./utils/sha.js")
+ , sha = require("sha")
, asyncMap = require("slide").asyncMap
, semver = require("semver")
, tar = require("./utils/tar.js")
, retry = require("retry")
, zlib = require("zlib")
, chmodr = require("chmodr")
+ , which = require("which")
cache.usage = "npm cache add <tarball file>"
+ "\nnpm cache add <folder>"
case "git+ftp:":
case "git+ssh:":
//p.protocol = p.protocol.replace(/^git([^:])/, "$1")
- return addRemoteGit(spec, p, name, cb)
+ return addRemoteGit(spec, p, name, false, cb)
default:
// if we have a name and a spec, then try name@spec
// if not, then try just spec (which may try name@"" if not found)
// 4. cd cacheDir && git fetch -a origin
// 5. git archive /tmp/random.tgz
// 6. addLocalTarball(/tmp/random.tgz) <gitref> --format=tar --prefix=package/
-function addRemoteGit (u, parsed, name, cb_) {
+// silent flag is used if this should error quietly
+function addRemoteGit (u, parsed, name, silent, cb_) {
if (typeof cb_ !== "function") cb_ = name, name = null
if (!inFlightURLs[u]) inFlightURLs[u] = []
p = path.join(npm.config.get("cache"), "_git-remotes", v)
- checkGitDir(p, u, co, origUrl, function(er, data) {
+ checkGitDir(p, u, co, origUrl, silent, function(er, data) {
chmodr(p, npm.modes.file, function(erChmod) {
if (er) return cb(er, data)
return cb(erChmod, data)
})
}
-function checkGitDir (p, u, co, origUrl, cb) {
+function checkGitDir (p, u, co, origUrl, silent, cb) {
fs.stat(p, function (er, s) {
- if (er) return cloneGitRemote(p, u, co, origUrl, cb)
+ if (er) return cloneGitRemote(p, u, co, origUrl, silent, cb)
if (!s.isDirectory()) return rm(p, function (er){
if (er) return cb(er)
- cloneGitRemote(p, u, co, origUrl, cb)
+ cloneGitRemote(p, u, co, origUrl, silent, cb)
})
var git = npm.config.get("git")
- var args = ["config", "--get", "remote.origin.url"]
+ var args = [ "config", "--get", "remote.origin.url" ]
var env = gitEnv()
- exec(git, args, env, false, p, function (er, code, stdout, stderr) {
- stdoutTrimmed = (stdout + "\n" + stderr).trim()
- if (er || u !== stdout.trim()) {
- log.warn( "`git config --get remote.origin.url` returned "
- + "wrong result ("+u+")", stdoutTrimmed )
- return rm(p, function (er){
- if (er) return cb(er)
- cloneGitRemote(p, u, co, origUrl, cb)
- })
+ // check for git
+ which(git, function (err) {
+ if (err) {
+ err.code = "ENOGIT"
+ return cb(err)
}
- log.verbose("git remote.origin.url", stdoutTrimmed)
- archiveGitRemote(p, u, co, origUrl, cb)
+ exec(git, args, {cwd: p, env: env}, function (er, stdout, stderr) {
+ stdoutTrimmed = (stdout + "\n" + stderr).trim()
+ if (er || u !== stdout.trim()) {
+ log.warn( "`git config --get remote.origin.url` returned "
+ + "wrong result ("+u+")", stdoutTrimmed )
+ return rm(p, function (er){
+ if (er) return cb(er)
+ cloneGitRemote(p, u, co, origUrl, silent, cb)
+ })
+ }
+ log.verbose("git remote.origin.url", stdoutTrimmed)
+ archiveGitRemote(p, u, co, origUrl, cb)
+ })
})
})
}
-function cloneGitRemote (p, u, co, origUrl, cb) {
+function cloneGitRemote (p, u, co, origUrl, silent, cb) {
mkdir(p, function (er) {
if (er) return cb(er)
- exec( npm.config.get("git"), ["clone", "--mirror", u, p], gitEnv(), false
- , function (er, code, stdout, stderr) {
- stdout = (stdout + "\n" + stderr).trim()
- if (er) {
- log.error("git clone " + u, stdout)
- return cb(er)
+
+ var git = npm.config.get("git")
+ var args = [ "clone", "--mirror", u, p ]
+ var env = gitEnv()
+
+ // check for git
+ which(git, function (err) {
+ if (err) {
+ err.code = "ENOGIT"
+ return cb(err)
}
- log.verbose("git clone " + u, stdout)
- archiveGitRemote(p, u, co, origUrl, cb)
+ exec(git, args, {cwd: p, env: env}, function (er, stdout, stderr) {
+ stdout = (stdout + "\n" + stderr).trim()
+ if (er) {
+ if (silent) {
+ log.verbose("git clone " + u, stdout)
+ } else {
+ log.error("git clone " + u, stdout)
+ }
+ return cb(er)
+ }
+ log.verbose("git clone " + u, stdout)
+ archiveGitRemote(p, u, co, origUrl, cb)
+ })
})
})
}
function archiveGitRemote (p, u, co, origUrl, cb) {
var git = npm.config.get("git")
- var archive = ["fetch", "-a", "origin"]
- var resolve = ["rev-list", "-n1", co]
+ var archive = [ "fetch", "-a", "origin" ]
+ var resolve = [ "rev-list", "-n1", co ]
var env = gitEnv()
var errState = null
var resolved = null
var tmp
- exec(git, archive, env, false, p, function (er, code, stdout, stderr) {
+ exec(git, archive, {cwd: p, env: env}, function (er, stdout, stderr) {
stdout = (stdout + "\n" + stderr).trim()
if (er) {
log.error("git fetch -a origin ("+u+")", stdout)
})
function resolveHead () {
- exec(git, resolve, env, false, p, function (er, code, stdout, stderr) {
+ exec(git, resolve, {cwd: p, env: env}, function (er, stdout, stderr) {
stdout = (stdout + "\n" + stderr).trim()
if (er) {
log.error("Failed resolving git HEAD (" + u + ")", stderr)
function maybeGithub (p, name, er, cb) {
var u = "git://github.com/" + p
, up = url.parse(u)
- log.info("maybeGithub", "Attempting to fetch %s from %s", p, u)
+ log.info("maybeGithub", "Attempting %s from %s", p, u)
+
+ return addRemoteGit(u, up, name, true, function (er2, data) {
+ if (er2) {
+ var upriv = "git+ssh://git@github.com:" + p
+ , uppriv = url.parse(upriv)
+
+ log.info("maybeGithub", "Attempting %s from %s", p, upriv)
- return addRemoteGit(u, up, name, function (er2, data) {
- if (er2) return cb(er)
+ return addRemoteGit(upriv, uppriv, false, name, function (er3, data) {
+ if (er3) return cb(er)
+ success(upriv, data)
+ })
+ }
+ success(u, data)
+ })
+
+ function success (u, data) {
data._from = u
data._fromGithub = true
return cb(null, data)
- })
+ }
}
function addLocalTarball (p, name, shasum, cb_) {
var log = require("npmlog")
, npm = require("./npm.js")
- , exec = require("./utils/exec.js")
+ , spawn = require("child_process").spawn
, fs = require("graceful-fs")
, npmconf = require("npmconf")
, types = npmconf.defs.types
, ini = require("ini")
+ , editor = require("editor")
config.completion = function (opts, cb) {
var argv = opts.conf.argv.remain
, "utf8"
, function (er) {
if (er) return cb(er)
- exec(e, [f], cb)
+ editor(f, { editor: e }, cb)
}
)
})
})
}
-var exec = require("./utils/exec.js")
- , npm = require("./npm.js")
+var npm = require("./npm.js")
, registry = npm.registry
, log = require("npmlog")
, opener = require("opener")
edit.completion = require("./utils/completion/installed-shallow.js")
var npm = require("./npm.js")
- , exec = require("./utils/exec.js")
+ , spawn = require("child_process").spawn
, path = require("path")
, fs = require("graceful-fs")
+ , editor = require("editor")
function edit (args, cb) {
var p = args[0]
if (args.length !== 1 || !p) return cb(edit.usage)
- var editor = npm.config.get("editor")
- if (!editor) return cb(new Error(
+ var e = npm.config.get("editor")
+ if (!e) return cb(new Error(
"No editor set. Set the 'editor' config, or $EDITOR environ."))
p = p.split("/")
.join("/node_modules/")
.replace(/(\/node_modules)+/, "/node_modules")
- fs.lstat(path.resolve(npm.dir, p), function (er) {
+ var f = path.resolve(npm.dir, p)
+ fs.lstat(f, function (er) {
if (er) return cb(er)
- exec(editor, [path.resolve(npm.dir, p)], function (er) {
+ editor(f, { editor: e }, function (er) {
if (er) return cb(er)
npm.commands.rebuild(args, cb)
})
-// npm expore <pkg>[@<version>]
+// npm explore <pkg>[@<version>]
// open a subshell to the package folder.
module.exports = explore
explore.completion = require("./utils/completion/installed-shallow.js")
var npm = require("./npm.js")
- , exec = require("./utils/exec.js")
+ , spawn = require("child_process").spawn
, path = require("path")
, fs = require("graceful-fs")
if (args) args = ["-c", args]
else args = []
- var editor = npm.config.get("editor")
- , cwd = path.resolve(npm.dir, p)
+ var cwd = path.resolve(npm.dir, p)
+ var s = npm.config.get("shell")
fs.stat(cwd, function (er, s) {
if (er || !s.isDirectory()) return cb(new Error(
"It doesn't look like "+p+" is installed."))
if (!args.length) console.log(
"\nExploring "+cwd+"\n"+
"Type 'exit' or ^D when finished\n")
- exec(npm.config.get("shell"), args, null, true, cwd, function (er) {
+
+ var shell = spawn(s, args, { cwd: cwd, customFds: [0, 1, 2] })
+ shell.on("close", function (er) {
// only fail if non-interactive.
if (!args.length) return cb()
cb(er)
var fs = require("graceful-fs")
, path = require("path")
- , exec = require("./utils/exec.js")
+ , spawn = require("child_process").spawn
, npm = require("./npm.js")
, log = require("npmlog")
, opener = require("opener")
switch (viewer) {
case "woman":
var a = ["-e", "(woman-find-file \"" + sectionPath + "\")"]
- exec("emacsclient", a, env, true, cb)
+ var conf = { env: env, customFds: [ 0, 1, 2] }
+ var woman = spawn("emacsclient", a, conf)
+ woman.on("close", cb)
break
case "browser":
break
default:
- exec("man", [num, section], env, true, cb)
+ var conf = { env: env, customFds: [ 0, 1, 2] }
+ var man = spawn("man", [num, section], conf)
+ man.on("close", cb)
}
}
)
,"Press ^C at any time to quit."
].join("\n"))
- initJson(dir, initFile, npm.config.get(), function (er, data) {
+ initJson(dir, initFile, npm.config, function (er, data) {
log.resume()
log.silly('package data', data)
log.info('init', 'written successfully')
}
+// monkey-patch support for 0.6 child processes
+require('child-process-close')
+
// FIXME there really ought to be a path.split in node core
require("path").SPLIT_CHAR = process.platform === "win32" ? "\\" : "/"
, npm = require("./npm.js")
, asyncMap = require("slide").asyncMap
, fs = require("graceful-fs")
- , exec = require("./utils/exec.js")
rebuild.usage = "npm rebuild [<name>[@<version>] [name[@<version>] ...]]"
module.exports = submodule
var npm = require("./npm.js")
- , exec = require("./utils/exec.js")
+ , exec = require("child_process").execFile
, cache = require("./cache.js")
, asyncMap = require("slide").asyncMap
, chain = require("slide").chain
+ , which = require("which")
submodule.usage = "npm submodule <pkg>"
}
function updateSubmodule (name, cb) {
- exec( npm.config.get("git"), [ "submodule", "update", "--init"
- , "node_modules/" + name ]
- , null, true, npm.prefix, cb)
+ var git = npm.config.get("git")
+ var args = [ "submodule", "update", "--init", "node_modules/", name ]
+
+ // check for git
+ which(git, function (err) {
+ if (err) {
+ err.code = "ENOGIT"
+ return cb(err)
+ }
+
+ exec(git, args, cb)
+ })
}
function addSubmodule (name, url, cb) {
- exec( npm.config.get("git"), [ "submodule", "add", url
- , "node_modules/" + name ]
- , null, true, npm.prefix, function (er) {
- if (er) return cb(er)
- updateSubmodule(name, cb)
+ var git = npm.config.get("git")
+ var args = [ "submodule", "add", url, "node_modules/", name ]
+
+ // check for git
+ which(git, function (err) {
+ if (err) {
+ err.code = "ENOGIT"
+ return cb(err)
+ }
+
+ exec(git, args, function (er) {
+ if (er) return cb(er)
+ updateSubmodule(name, cb)
+ })
})
}
var getSubmodules = function getSubmodules (cb) {
- exec( npm.config.get("git"), ["submodule", "status"], null, false
- , npm.prefix, function (er, code, stdout, stderr) {
- if (er) return cb(er)
- res = stdout.trim().split(/\n/).map(function (line) {
- return line.trim().split(/\s+/)[1]
- }).filter(function (line) {
- // only care about submodules in the node_modules folder.
- return line && line.match(/^node_modules\//)
- }).map(function (line) {
- return line.replace(/^node_modules\//g, "")
+ var git = npm.config.get("git")
+ var arg = [ "submodule", "status" ]
+
+ // check for git
+ which(git, function (err) {
+ if (err) {
+ err.code = "ENOGIT"
+ return cb(err)
+ }
+ exec(git, args, function (er, stdout, stderr) {
+ if (er) return cb(er)
+ res = stdout.trim().split(/\n/).map(function (line) {
+ return line.trim().split(/\s+/)[1]
+ }).filter(function (line) {
+ // only care about submodules in the node_modules folder.
+ return line && line.match(/^node_modules\//)
+ }).map(function (line) {
+ return line.replace(/^node_modules\//g, "")
+ })
+
+ // memoize.
+ getSubmodules = function (cb) { return cb(null, res) }
+
+ cb(null, res)
})
-
- // memoize.
- getSubmodules = function (cb) { return cb(null, res) }
-
- cb(null, res)
})
}
+++ /dev/null
-
-module.exports = remotePackages
-
-var npm = require("../../npm.js")
- , registry = npm.registry
- , containsSingleMatch = require("./contains-single-match.js")
- , getCompletions = require("./get-completions.js")
-
-/*
- Looks up remote packages for CLI tab-completion.
-
- NOTE: If doVersion is true, versions in the form <name>@<version>
- will be completed.
-
- If doTag is true, tags in the form <name>@<tag> will be
- completed.
-
- If recurring in true, sequences of multiple packages can be
- completed. i.e. for schemes such as:
- npm <command> <name>[@<version> [<name>[@<version>] ...]
-*/
-function remotePackages (args, index, doVersion, doTag
- , recurring, cb) {
- if (recurring || index < 3) {
- var name = (args.length + 1 === index) ? args[args.length - 1] : ""
- if (name === undefined) name = ""
- if (name.indexOf("/") !== -1) return cb(null, [])
- // use up-to 1 hour stale cache. not super urgent.
- registry.get("/", 3600, function (er, d) {
- if (er) return cb(er)
- var remoteList = Object.keys(d)
- , found = remoteList.indexOf(name)
- , unique = found && containsSingleMatch(name, remoteList)
- , simpleMatches = getCompletions(name, remoteList)
- , uniqueMatch = unique && simpleMatches[0]
- , addTag = doTag && (unique || found || name.indexOf("@") !== -1)
- , addVer = doVersion && (unique || found || name.indexOf("@") !== -1)
- , list = []
- , pieces = (uniqueMatch || name).split("@")
- , pkgname = pieces[0]
- , extras = []
- if (unique && !addTag && !addVer) return cb(null, [uniqueMatch])
- if (d[pkgname] && (addTag || addVer)) {
- if (d[pkgname].versions && addVer) {
- extras = extras.concat(Object.keys(d[pkgname].versions))
- }
- if (d[pkgname]["dist-tags"] && addTag) {
- extras = extras.concat(Object.keys(d[pkgname]["dist-tags"]))
- }
- list = getCompletions(name, list.concat(extras.map(function (e) {
- return pkgname + "@" + e
- })))
- }
- if (!unique) list = list.concat(getCompletions(name, remoteList))
- return cb(null, list)
- })
- }
-}
+++ /dev/null
-
-module.exports = users
-
-var npm = require("../../npm")
- , registry = npm.registry
- , containsSingleMatch = require("./contains-single-match.js")
- , getCompletions = require("./get-completions.js")
- , log = require("npmlog")
-
-function users (args, index, cb) {
- var name = (args.length + 1 === index) ? args[args.length - 1] : ""
- if (name === undefined) name = ""
- // use up-to 1 day stale cache. doesn't change much
- log.warn("users completion", "About to fetch")
- registry.get("/-/users", 24*60*60, function (er, d) {
- log.warn("userdata", d)
- log.warn("name", name)
- if (er) return cb(er)
- var remoteList = Object.keys(d)
- , simpleMatches = getCompletions(name, remoteList)
- return cb(null, simpleMatches)
- })
-}
].join("\n"))
break
+ case "ENOGIT":
+ er.code = "ENOGIT"
+ log.error("", er.message)
+ log.error("", ["","Failed using git."
+ ,"This is most likely not a problem with npm itself."
+ ,"Please check if you have git installed and in your PATH."
+ ].join("\n"))
+ break
+
case "EJSONPARSE":
er.code = "EJSONPARSE"
log.error("", er.message)
log.error("peerinvalid", [er.message].concat(peerErrors).join("\n"))
break
+ case "ENOTFOUND":
+ log.error("network", [er.message
+ ,"This is most likely not a problem with npm itself"
+ ,"and is related to network connectivity."
+ ,"In most cases you are behind a proxy or have bad network settings."
+ ,"\nIf you are behind a proxy, please make sure that the"
+ ,"'proxy' config is set properly. See: 'npm help config'"
+ ].join("\n"))
+ break
+
case "ENOTSUP":
if (er.required) {
log.error("notsup", [er.message
+++ /dev/null
-module.exports = exec
-exec.spawn = spawn
-
-var log = require("npmlog")
- , child_process = require("child_process")
- , util = require("util")
- , npm = require("../npm.js")
- , myUID = process.getuid ? process.getuid() : null
- , myGID = process.getgid ? process.getgid() : null
- , isRoot = process.getuid && myUID === 0
- , constants = require("constants")
- , uidNumber = require("uid-number")
- , once = require("once")
-
-function exec (cmd, args, env, takeOver, cwd, uid, gid, cb) {
- if (typeof cb !== "function") cb = gid, gid = null
- if (typeof cb !== "function") cb = uid, uid = null
- if (typeof cb !== "function") cb = cwd, cwd = null
- if (typeof cb !== "function") cb = takeOver, takeOver = true
- if (typeof cb !== "function") cb = env, env = process.env
- gid = gid == null ? myGID : gid
- uid = uid == null ? myUID : uid
- if (!isRoot) {
- if (npm.config.get("unsafe-perm")) {
- uid = myUID
- gid = myGID
- } else if (uid !== myUID || gid !== myGID) {
- var e = new Error("EPERM: setuid() operation not permitted")
- e.errno = constants.EPERM
- return cb(e)
- }
- }
- if (uid !== myUID) {
- log.verbose("set uid", "from=%s to=%s", myUID, uid)
- }
-
- if (uid && gid && (isNaN(uid) || isNaN(gid))) {
- // get the numeric values
- return uidNumber(uid, gid, function (er, uid, gid) {
- if (er) return cb(er)
- exec(cmd, args, env, takeOver, cwd, uid, gid, cb)
- })
- }
-
- log.silly("exec", cmd+" "+args.map(JSON.stringify).join(" "))
- var stdout = ""
- , stderr = ""
- , cp = spawn(cmd, args, env, takeOver, cwd, uid, gid)
- cb = once(cb)
- cp.on("error", cb)
- cp.stdout && cp.stdout.on("data", function (chunk) {
- if (chunk) stdout += chunk
- })
- cp.stderr && cp.stderr.on("data", function (chunk) {
- if (chunk) stderr += chunk
- })
- cp.on("close", function (code) {
- var er = null
- if (code) er = new Error("`"+cmd
- +(args.length ? " "
- + args.map(JSON.stringify).join(" ")
- : "")
- +"` failed with "+code)
- cb(er, code, stdout, stderr)
- })
- return cp
-}
-
-
-function spawn (c, a, env, takeOver, cwd, uid, gid) {
- var fds = [ 0, 1, 2 ]
- , opts = { customFds : takeOver ? fds : [-1,-1,-1]
- , env : env || process.env
- , cwd : cwd || null }
- , cp
-
- if (uid && !isNaN(uid)) opts.uid = +uid
- if (gid && !isNaN(gid)) opts.gid = +gid
-
- var name = c +" "+ a.map(JSON.stringify).join(" ")
- log.silly([c, a, opts.cwd], "spawning")
- cp = child_process.spawn(c, a, opts)
- cp.name = name
- return cp
-}
fstr.on("error", function (er) {
cb(er)
- fstr.close()
+ fstr.destroy()
})
var req = makeRequest(remote, fstr, headers)
}
fs.readdir(p, function (er, files) {
// an error right away is a bad sign.
- if (er && p === original) return cb(er)
+ // unless the prefix was simply a non
+ // existent directory.
+ if (er && p === original) {
+ if (er.code === "ENOENT") return cb(null, original);
+ return cb(er)
+ }
// walked up too high or something.
if (er) return cb(null, original)
exports.cmd = cmd
var log = require("npmlog")
- , exec = require("./exec.js")
+ , spawn = require("child_process").spawn
, npm = require("../npm.js")
, path = require("path")
, fs = require("graceful-fs")
+ "\n> " + cmd + "\n"
console.log(note)
- exec( sh, [shFlag, cmd], env, true, wd
- , user, group
- , function (er, code, stdout, stderr) {
+
+ var conf = { cwd: wd, env: env, customFds: [ 0, 1, 2] }
+ var proc = spawn(sh, [shFlag, cmd], conf)
+ proc.on("close", function (er, stdout, stderr) {
if (er && !npm.ROLLBACK) {
log.info(pkg._id, "Failed to exec "+stage+" script")
er.message = pkg._id + " "
fs.stat(hook, function (er) {
if (er) return cb()
- exec( "sh", ["-c", cmd], env, true, wd
- , user, group
- , function (er) {
+ var conf = { cwd: wd, env: env, customFds: [ 0, 1, 2] }
+ var proc = spawn("sh", ["-c", cmd], conf)
+ proc.on("close", function (er) {
if (er) {
er.message += "\nFailed to exec "+stage+" hook script"
log.info(pkg._id, er)
+++ /dev/null
-
-var fs = require("graceful-fs")
- , crypto = require("crypto")
- , log = require("npmlog")
- , binding
-
-try { binding = process.binding("crypto") }
-catch (e) {
- var er = new Error( "crypto binding not found.\n"
- + "Please build node with openssl.\n"
- + e.message )
- throw er
-}
-
-exports.check = check
-exports.get = get
-
-function check (file, sum, cb) {
- get(file, function (er, actual) {
- if (er) {
- log.error("shasum", "error getting shasum")
- return cb(er)
- }
- var expected = sum.toLowerCase().trim()
- , ok = actual === expected
- cb(ok ? null : new Error(
- "shasum check failed for "+file+"\n"
- +"Expected: "+expected+"\n"
- +"Actual: "+actual))
- })
-}
-
-function get (file, cb) {
- var h = crypto.createHash("sha1")
- , s = fs.createReadStream(file)
- , errState = null
- s.on("error", function (er) {
- if (errState) return
- log.silly("shasum", "error", er)
- return cb(errState = er)
- }).on("data", function (chunk) {
- if (errState) return
- log.silly("shasum", "updated bytes", chunk.length)
- h.update(chunk)
- }).on("end", function () {
- if (errState) return
- var actual = h.digest("hex").toLowerCase().trim()
- log.info("shasum", actual+"\n"+file)
- cb(null, actual)
- })
-}
module.exports = version
-var exec = require("./utils/exec.js")
+var exec = require("child_process").execFile
, semver = require("semver")
, path = require("path")
, fs = require("graceful-fs")
, chain = require("slide").chain
, log = require("npmlog")
+ , which = require("which")
, npm = require("./npm.js")
version.usage = "npm version [<newversion> | major | minor | patch | build]\n"
}
function checkGit (data, cb) {
- exec( npm.config.get("git"), ["status", "--porcelain"], process.env, false
- , function (er, code, stdout, stderr) {
- var lines = stdout.trim().split("\n").filter(function (line) {
- return line.trim() && !line.match(/^\?\? /)
- }).map(function (line) {
- return line.trim()
- })
- if (lines.length) return cb(new Error(
- "Git working directory not clean.\n"+lines.join("\n")))
- write(data, function (er) {
- if (er) return cb(er)
- var message = npm.config.get("message").replace(/%s/g, data.version)
- , sign = npm.config.get("sign-git-tag")
- , flag = sign ? "-sm" : "-am"
- chain
- ( [ [ exec, npm.config.get("git")
- , ["add","package.json"], process.env, false ]
- , [ exec, npm.config.get("git")
- , ["commit", "-m", message ], process.env, false ]
- , [ exec, npm.config.get("git")
- , ["tag", "v"+data.version, flag, message], process.env, false ] ]
- , cb )
- })
+ var git = npm.config.get("git")
+ var args = [ "status", "--porcelain" ]
+ var env = process.env
+
+ // check for git
+ which(git, function (err) {
+ if (err) {
+ err.code = "ENOGIT"
+ return cb(err)
+ }
+
+ gitFound()
})
+
+ function gitFound () {
+ exec(git, args, {env: env}, function (er, stdout, stderr) {
+ var lines = stdout.trim().split("\n").filter(function (line) {
+ return line.trim() && !line.match(/^\?\? /)
+ }).map(function (line) {
+ return line.trim()
+ })
+ if (lines.length) return cb(new Error(
+ "Git working directory not clean.\n"+lines.join("\n")))
+ write(data, function (er) {
+ if (er) return cb(er)
+ var message = npm.config.get("message").replace(/%s/g, data.version)
+ , sign = npm.config.get("sign-git-tag")
+ , flag = sign ? "-sm" : "-am"
+ chain
+ ( [ [ exec, git, [ "add", "package.json" ], {env: process.env} ]
+ , [ exec, git, [ "commit", "-m", message ], {env: process.env} ]
+ , [ exec, git, [ "tag", "v" + data.version, flag, message ]
+ , {env: process.env} ] ]
+ , cb )
+ })
+ })
+ }
}
function write (data, cb) {
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM" "1" "April 2013" "" ""
+.TH "NPM" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm\fR \-\- node package manager
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-ADDUSER" "1" "April 2013" "" ""
+.TH "NPM\-ADDUSER" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-adduser\fR \-\- Add a registry user account
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BIN" "1" "April 2013" "" ""
+.TH "NPM\-BIN" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-bin\fR \-\- Display npm bin folder
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUGS" "1" "April 2013" "" ""
+.TH "NPM\-BUGS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-bugs\fR \-\- Bugs for a package in a web browser maybe
.SS "browser"
.
.IP "\(bu" 4
-Default: OS X: \fB"open"\fR, others: \fB"google\-chrome"\fR
+Default: OS X: \fB"open"\fR, Windows: \fB"start"\fR, Others: \fB"xdg\-open"\fR
.
.IP "\(bu" 4
Type: String
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUILD" "1" "April 2013" "" ""
+.TH "NPM\-BUILD" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-build\fR \-\- Build a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUNDLE" "1" "April 2013" "" ""
+.TH "NPM\-BUNDLE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-bundle\fR \-\- REMOVED
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CACHE" "1" "April 2013" "" ""
+.TH "NPM\-CACHE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-cache\fR \-\- Manipulates packages cache
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CHANGELOG" "1" "April 2013" "" ""
+.TH "NPM\-CHANGELOG" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-changelog\fR \-\- Changes
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CODING\-STYLE" "1" "April 2013" "" ""
+.TH "NPM\-CODING\-STYLE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-coding-style\fR \-\- npm\'s "funny" coding style
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-COMPLETION" "1" "April 2013" "" ""
+.TH "NPM\-COMPLETION" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-completion\fR \-\- Tab Completion for npm
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CONFIG" "1" "April 2013" "" ""
+.TH "NPM\-CONFIG" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-config\fR \-\- Manage the npm configuration file
.SS "browser"
.
.IP "\(bu" 4
-Default: OS X: \fB"open"\fR, others: \fB"google\-chrome"\fR
+Default: OS X: \fB"open"\fR, Windows: \fB"start"\fR, Others: \fB"xdg\-open"\fR
.
.IP "\(bu" 4
Type: String
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEDUPE" "1" "April 2013" "" ""
+.TH "NPM\-DEDUPE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-dedupe\fR \-\- Reduce duplication
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEPRECATE" "1" "April 2013" "" ""
+.TH "NPM\-DEPRECATE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-deprecate\fR \-\- Deprecate a version of a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEVELOPERS" "1" "April 2013" "" ""
+.TH "NPM\-DEVELOPERS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-developers\fR \-\- Developer Guide
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DISPUTES" "1" "April 2013" "" ""
+.TH "NPM\-DISPUTES" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-disputes\fR \-\- Handling Module Name Disputes
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DOCS" "1" "April 2013" "" ""
+.TH "NPM\-DOCS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-docs\fR \-\- Docs for a package in a web browser maybe
.SS "browser"
.
.IP "\(bu" 4
-Default: OS X: \fB"open"\fR, others: \fB"google\-chrome"\fR
+Default: OS X: \fB"open"\fR, Windows: \fB"start"\fR, Others: \fB"xdg\-open"\fR
.
.IP "\(bu" 4
Type: String
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EDIT" "1" "April 2013" "" ""
+.TH "NPM\-EDIT" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-edit\fR \-\- Edit an installed package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EXPLORE" "1" "April 2013" "" ""
+.TH "NPM\-EXPLORE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-explore\fR \-\- Browse an installed package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-FAQ" "1" "April 2013" "" ""
+.TH "NPM\-FAQ" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-faq\fR \-\- Frequently Asked Questions
an argument to \fBgit checkout\fR\|\. The default is \fBmaster\fR\|\.
.
.SH "How do I install node with npm?"
-You don\'t\. Try one of these:
+You don\'t\. Try one of these node version managers:
+.
+.P
+Unix:
+.
+.IP "\(bu" 4
+\fIhttp://github\.com/isaacs/nave\fR
+.
+.IP "\(bu" 4
+\fIhttp://github\.com/visionmedia/n\fR
.
.IP "\(bu" 4
-\fIhttps://github\.com/isaacs/nave\fR
+\fIhttp://github\.com/creationix/nvm\fR
+.
+.IP "" 0
+.
+.P
+Windows:
.
.IP "\(bu" 4
-\fIhttps://github\.com/visionmedia/n\fR
+\fIhttp://github\.com/marcelklehr/nodist\fR
.
.IP "\(bu" 4
-\fIhttps://github\.com/creationix/nvm\fR
+\fIhttps://github\.com/hakobera/nvmw\fR
.
.IP "" 0
.
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-FOLDERS" "1" "April 2013" "" ""
+.TH "NPM\-FOLDERS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-folders\fR \-\- Folder Structures Used by npm
+\-\- node_modules
+\-\- blerg (1\.2\.5) <\-\-\-[A]
+\-\- bar (1\.2\.3) <\-\-\-[B]
- | +\-\- node_modules
- | | `\-\- baz (2\.0\.2) <\-\-\-[C]
- | | `\-\- node_modules
- | | `\-\- quux (3\.2\.0)
- | `\-\- asdf (2\.3\.4)
+ | `\-\- node_modules
+ | +\-\- baz (2\.0\.2) <\-\-\-[C]
+ | | `\-\- node_modules
+ | | `\-\- quux (3\.2\.0)
+ | `\-\- asdf (2\.3\.4)
`\-\- baz (1\.2\.3) <\-\-\-[D]
`\-\- node_modules
`\-\- quux (3\.2\.0) <\-\-\-[E]
.IP "" 0
.
.P
-Since foo depends directly on bar@1\.2\.3 and baz@1\.2\.3, those are
+Since foo depends directly on \fBbar@1\.2\.3\fR and \fBbaz@1\.2\.3\fR, those are
installed in foo\'s \fBnode_modules\fR folder\.
.
.P
Even though the latest copy of blerg is 1\.3\.7, foo has a specific
dependency on version 1\.2\.5\. So, that gets installed at [A]\. Since the
-parent installation of blerg satisfie\'s bar\'s dependency on blerg@1\.x,
+parent installation of blerg satisfies bar\'s dependency on \fBblerg@1\.x\fR,
it does not install another copy under [B]\.
.
.P
and must install its own copy [C]\.
.
.P
-Underneath bar, the \fBbaz\->quux\->bar\fR dependency creates a cycle\.
-However, because \fBbar\fR is already in \fBquux\fR\'s ancestry [B], it does not
+Underneath bar, the \fBbaz \-> quux \-> bar\fR dependency creates a cycle\.
+However, because bar is already in quux\'s ancestry [B], it does not
unpack another copy of bar into that folder\.
.
.P
-Underneath \fBfoo\->baz\fR [D], quux\'s [E] folder tree is empty, because its
+Underneath \fBfoo \-> baz\fR [D], quux\'s [E] folder tree is empty, because its
dependency on bar is satisfied by the parent folder copy installed at [B]\.
.
.P
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-FOLDERS" "1" "April 2013" "" ""
+.TH "NPM\-FOLDERS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-folders\fR \-\- Folder Structures Used by npm
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-HELP\-SEARCH" "1" "April 2013" "" ""
+.TH "NPM\-HELP\-SEARCH" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-help-search\fR \-\- Search npm help documentation
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-HELP" "1" "April 2013" "" ""
+.TH "NPM\-HELP" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-help\fR \-\- Get help on npm
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INDEX" "1" "April 2013" "" ""
+.TH "NPM\-INDEX" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-index\fR \-\- Index of all npm documentation
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INIT" "1" "April 2013" "" ""
+.TH "NPM\-INIT" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-init\fR \-\- Interactively create a package\.json file
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INSTALL" "1" "April 2013" "" ""
+.TH "NPM\-INSTALL" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-install\fR \-\- Install a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-JSON" "1" "April 2013" "" ""
+.TH "NPM\-JSON" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-json\fR \-\- Specifics of npm\'s package\.json handling
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LINK" "1" "April 2013" "" ""
+.TH "NPM\-LINK" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-link\fR \-\- Symlink a package folder
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LS" "1" "April 2013" "" ""
+.TH "NPM\-LS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-ls\fR \-\- List installed packages
.IP "" 4
.
.nf
-npm@1.2.18 /path/to/npm
+npm@1.2.20 /path/to/npm
└─┬ init\-package\-json@0\.0\.4
└── promzard@0\.1\.5
.
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM" "1" "April 2013" "" ""
+.TH "NPM" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm\fR \-\- node package manager
.fi
.
.SH "VERSION"
-1.2.18
+1.2.20
.
.SH "DESCRIPTION"
npm is the package manager for the Node JavaScript platform\. It puts
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OUTDATED" "1" "April 2013" "" ""
+.TH "NPM\-OUTDATED" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-outdated\fR \-\- Check for outdated packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OWNER" "1" "April 2013" "" ""
+.TH "NPM\-OWNER" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-owner\fR \-\- Manage package owners
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PACK" "1" "April 2013" "" ""
+.TH "NPM\-PACK" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-pack\fR \-\- Create a tarball from a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PREFIX" "1" "April 2013" "" ""
+.TH "NPM\-PREFIX" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-prefix\fR \-\- Display prefix
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PRUNE" "1" "April 2013" "" ""
+.TH "NPM\-PRUNE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-prune\fR \-\- Remove extraneous packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PUBLISH" "1" "April 2013" "" ""
+.TH "NPM\-PUBLISH" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-publish\fR \-\- Publish a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REBUILD" "1" "April 2013" "" ""
+.TH "NPM\-REBUILD" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-rebuild\fR \-\- Rebuild a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REGISTRY" "1" "April 2013" "" ""
+.TH "NPM\-REGISTRY" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-registry\fR \-\- The JavaScript Package Registry
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REMOVAL" "1" "April 2013" "" ""
+.TH "NPM\-REMOVAL" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-removal\fR \-\- Cleaning the Slate
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RESTART" "1" "April 2013" "" ""
+.TH "NPM\-RESTART" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-restart\fR \-\- Start a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RM" "1" "April 2013" "" ""
+.TH "NPM\-RM" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-rm\fR \-\- Remove a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-ROOT" "1" "April 2013" "" ""
+.TH "NPM\-ROOT" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-root\fR \-\- Display npm root
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RUN\-SCRIPT" "1" "April 2013" "" ""
+.TH "NPM\-RUN\-SCRIPT" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-run-script\fR \-\- Run arbitrary package scripts
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SCRIPTS" "1" "April 2013" "" ""
+.TH "NPM\-SCRIPTS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-scripts\fR \-\- How npm handles the "scripts" field
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SEARCH" "1" "April 2013" "" ""
+.TH "NPM\-SEARCH" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-search\fR \-\- Search for packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SEMVER" "1" "April 2013" "" ""
+.TH "NPM\-SEMVER" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-semver\fR \-\- The semantic versioner for npm
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SHRINKWRAP" "1" "April 2013" "" ""
+.TH "NPM\-SHRINKWRAP" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-shrinkwrap\fR \-\- Lock down dependency versions
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STAR" "1" "April 2013" "" ""
+.TH "NPM\-STAR" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-star\fR \-\- Mark your favorite packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STARS" "1" "April 2013" "" ""
+.TH "NPM\-STARS" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-stars\fR \-\- View packages marked as favorites
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-START" "1" "April 2013" "" ""
+.TH "NPM\-START" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-start\fR \-\- Start a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STOP" "1" "April 2013" "" ""
+.TH "NPM\-STOP" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-stop\fR \-\- Stop a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SUBMODULE" "1" "April 2013" "" ""
+.TH "NPM\-SUBMODULE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-submodule\fR \-\- Add a package as a git submodule
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TAG" "1" "April 2013" "" ""
+.TH "NPM\-TAG" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-tag\fR \-\- Tag a published version
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TEST" "1" "April 2013" "" ""
+.TH "NPM\-TEST" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-test\fR \-\- Test a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RM" "1" "April 2013" "" ""
+.TH "NPM\-RM" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-rm\fR \-\- Remove a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UNPUBLISH" "1" "April 2013" "" ""
+.TH "NPM\-UNPUBLISH" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-unpublish\fR \-\- Remove a package from the registry
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UPDATE" "1" "April 2013" "" ""
+.TH "NPM\-UPDATE" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-update\fR \-\- Update a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VERSION" "1" "April 2013" "" ""
+.TH "NPM\-VERSION" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-version\fR \-\- Bump a package version
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VIEW" "1" "April 2013" "" ""
+.TH "NPM\-VIEW" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-view\fR \-\- View registry info
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-WHOAMI" "1" "April 2013" "" ""
+.TH "NPM\-WHOAMI" "1" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-whoami\fR \-\- Display npm username
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BIN" "3" "April 2013" "" ""
+.TH "NPM\-BIN" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-bin\fR \-\- Display npm bin folder
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-BUGS" "3" "April 2013" "" ""
+.TH "NPM\-BUGS" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-bugs\fR \-\- Bugs for a package in a web browser maybe
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-COMMANDS" "3" "April 2013" "" ""
+.TH "NPM\-COMMANDS" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-commands\fR \-\- npm commands
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-CONFIG" "3" "April 2013" "" ""
+.TH "NPM\-CONFIG" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-config\fR \-\- Manage the npm configuration files
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DEPRECATE" "3" "April 2013" "" ""
+.TH "NPM\-DEPRECATE" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-deprecate\fR \-\- Deprecate a version of a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-DOCS" "3" "April 2013" "" ""
+.TH "NPM\-DOCS" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-docs\fR \-\- Docs for a package in a web browser maybe
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EDIT" "3" "April 2013" "" ""
+.TH "NPM\-EDIT" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-edit\fR \-\- Edit an installed package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-EXPLORE" "3" "April 2013" "" ""
+.TH "NPM\-EXPLORE" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-explore\fR \-\- Browse an installed package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-HELP\-SEARCH" "3" "April 2013" "" ""
+.TH "NPM\-HELP\-SEARCH" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-help-search\fR \-\- Search the help pages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "INIT" "3" "April 2013" "" ""
+.TH "INIT" "3" "May 2013" "" ""
.
.SH "NAME"
\fBinit\fR \-\- Interactively create a package\.json file
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-INSTALL" "3" "April 2013" "" ""
+.TH "NPM\-INSTALL" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-install\fR \-\- install a package programmatically
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LINK" "3" "April 2013" "" ""
+.TH "NPM\-LINK" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-link\fR \-\- Symlink a package folder
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LOAD" "3" "April 2013" "" ""
+.TH "NPM\-LOAD" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-load\fR \-\- Load config settings
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-LS" "3" "April 2013" "" ""
+.TH "NPM\-LS" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-ls\fR \-\- List installed packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM" "3" "April 2013" "" ""
+.TH "NPM" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm\fR \-\- node package manager
.fi
.
.SH "VERSION"
-1.2.18
+1.2.20
.
.SH "DESCRIPTION"
This is the API documentation for npm\.
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OUTDATED" "3" "April 2013" "" ""
+.TH "NPM\-OUTDATED" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-outdated\fR \-\- Check for outdated packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-OWNER" "3" "April 2013" "" ""
+.TH "NPM\-OWNER" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-owner\fR \-\- Manage package owners
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PACK" "3" "April 2013" "" ""
+.TH "NPM\-PACK" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-pack\fR \-\- Create a tarball from a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PREFIX" "3" "April 2013" "" ""
+.TH "NPM\-PREFIX" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-prefix\fR \-\- Display prefix
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PRUNE" "3" "April 2013" "" ""
+.TH "NPM\-PRUNE" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-prune\fR \-\- Remove extraneous packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-PUBLISH" "3" "April 2013" "" ""
+.TH "NPM\-PUBLISH" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-publish\fR \-\- Publish a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-REBUILD" "3" "April 2013" "" ""
+.TH "NPM\-REBUILD" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-rebuild\fR \-\- Rebuild a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RESTART" "3" "April 2013" "" ""
+.TH "NPM\-RESTART" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-restart\fR \-\- Start a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-ROOT" "3" "April 2013" "" ""
+.TH "NPM\-ROOT" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-root\fR \-\- Display npm root
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-RUN\-SCRIPT" "3" "April 2013" "" ""
+.TH "NPM\-RUN\-SCRIPT" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-run-script\fR \-\- Run arbitrary package scripts
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SEARCH" "3" "April 2013" "" ""
+.TH "NPM\-SEARCH" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-search\fR \-\- Search for packages
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SHRINKWRAP" "3" "April 2013" "" ""
+.TH "NPM\-SHRINKWRAP" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-shrinkwrap\fR \-\- programmatically generate package shrinkwrap file
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-START" "3" "April 2013" "" ""
+.TH "NPM\-START" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-start\fR \-\- Start a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-STOP" "3" "April 2013" "" ""
+.TH "NPM\-STOP" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-stop\fR \-\- Stop a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-SUBMODULE" "3" "April 2013" "" ""
+.TH "NPM\-SUBMODULE" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-submodule\fR \-\- Add a package as a git submodule
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TAG" "3" "April 2013" "" ""
+.TH "NPM\-TAG" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-tag\fR \-\- Tag a published version
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-TEST" "3" "April 2013" "" ""
+.TH "NPM\-TEST" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-test\fR \-\- Test a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UNINSTALL" "3" "April 2013" "" ""
+.TH "NPM\-UNINSTALL" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-uninstall\fR \-\- uninstall a package programmatically
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UNPUBLISH" "3" "April 2013" "" ""
+.TH "NPM\-UNPUBLISH" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-unpublish\fR \-\- Remove a package from the registry
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-UPDATE" "3" "April 2013" "" ""
+.TH "NPM\-UPDATE" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-update\fR \-\- Update a package
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VERSION" "3" "April 2013" "" ""
+.TH "NPM\-VERSION" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-version\fR \-\- Bump a package version
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-VIEW" "3" "April 2013" "" ""
+.TH "NPM\-VIEW" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-view\fR \-\- View registry info
.\" Generated with Ronnjs 0.3.8
.\" http://github.com/kapouer/ronnjs/
.
-.TH "NPM\-WHOAMI" "3" "April 2013" "" ""
+.TH "NPM\-WHOAMI" "3" "May 2013" "" ""
.
.SH "NAME"
\fBnpm-whoami\fR \-\- Display npm username
--- /dev/null
+
+# child-process-close
+
+This module makes child process objects, (created with `spawn`, `fork`, `exec`
+or `execFile`) emit the `close` event in node v0.6 like they do in node v0.8.
+This makes it easier to write code that works correctly on both version of
+node.
+
+
+## Usage
+
+Just make sure to `require('child-process-close')` anywhere. It will patch the `child_process` module.
+
+```js
+require('child-process-close');
+var spawn = require('child_process').spawn;
+
+var cp = spawn('foo');
+cp.on('close', function(exitCode, signal) {
+ // This now works on all node versions.
+});
+```
+
+
+## License
+
+Copyright (C) 2012 Bert Belder
+
+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 child_process = require('child_process');
+
+// Re-export the child_process module.
+module.exports = child_process;
+
+// Only node versions up to v0.7.6 need this hook.
+if (!/^v0\.([0-6]\.|7\.[0-6](\D|$))/.test(process.version))
+ return;
+
+// Do not add the hook if already hooked.
+if (child_process.hasOwnProperty('_exit_hook'))
+ return;
+
+// Version the hook in case there is ever the need to release a 0.2.0.
+child_process._exit_hook = 1;
+
+
+function hook(name) {
+ var orig = child_process[name];
+
+ // Older node versions may not have all functions, e.g. fork().
+ if (!orig)
+ return;
+
+ // Store the unhooked version.
+ child_process['_original_' + name] = orig;
+
+ // Do the actual hooking.
+ child_process[name] = function() {
+ var child = orig.apply(this, arguments);
+
+ child.once('exit', function(code, signal) {
+ process.nextTick(function() {
+ child.emit('close', code, signal);
+ });
+ });
+
+ return child;
+ }
+}
+
+hook('spawn');
+hook('fork');
+hook('execFile');
+
+// Don't hook 'exec': it calls `exports.execFile` internally, so hooking it
+// would trigger the close event twice.
--- /dev/null
+{
+ "name": "child-process-close",
+ "version": "0.1.1",
+ "description": "Make child_process objects emit 'close' events in node v0.6 like they do in v0.8. This makes it easier to write code that works correctly on both version of node.",
+ "main": "index.js",
+ "scripts": {
+ "test": "node test/test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/piscisaureus/child-process-close.git"
+ },
+ "keywords": [
+ "child_process",
+ "spawn",
+ "fork",
+ "exec",
+ "execFile",
+ "close",
+ "exit"
+ ],
+ "author": {
+ "name": "Bert Belder"
+ },
+ "license": "MIT",
+ "readme": "\n# child-process-close\n\nThis module makes child process objects, (created with `spawn`, `fork`, `exec`\nor `execFile`) emit the `close` event in node v0.6 like they do in node v0.8.\nThis makes it easier to write code that works correctly on both version of\nnode.\n\n\n## Usage\n\nJust make sure to `require('child-process-close')` anywhere. It will patch the `child_process` module.\n\n```js\nrequire('child-process-close');\nvar spawn = require('child_process').spawn;\n\nvar cp = spawn('foo');\ncp.on('close', function(exitCode, signal) {\n // This now works on all node versions.\n});\n```\n\n\n## License\n\nCopyright (C) 2012 Bert Belder\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/piscisaureus/child-process-close/issues"
+ },
+ "_id": "child-process-close@0.1.1",
+ "dist": {
+ "shasum": "c1909c6c3bbcea623e3bd74493ddb1c94c47c500"
+ },
+ "_from": "child-process-close@",
+ "_resolved": "https://registry.npmjs.org/child-process-close/-/child-process-close-0.1.1.tgz"
+}
--- /dev/null
+
+require('../index');
+
+var assert = require('assert'),
+ exec = require('child_process').exec;
+
+var cp = exec('echo hello', function(err) {
+ assert(!err);
+});
+
+var stdoutData = '',
+ stdoutEnd = false,
+ gotExit = false,
+ gotClose = false;
+
+cp.stdout.setEncoding('ascii');
+
+cp.stdout.on('data', function(data) {
+ assert(!stdoutEnd);
+ stdoutData += data;
+});
+
+cp.stdout.on('end', function() {
+ assert(!stdoutEnd);
+ assert(/^hello/.test(stdoutData));
+ stdoutEnd = true;
+});
+
+cp.on('exit', function(code, signal) {
+ assert.strictEqual(code, 0);
+ assert(!signal);
+ assert(!gotExit);
+ assert(!gotClose);
+ gotExit = true;
+});
+
+cp.on('close', function(code, signal) {
+ assert.strictEqual(code, 0);
+ assert(!signal);
+ assert(gotExit);
+ assert(stdoutEnd);
+ assert(!gotClose);
+ gotClose = true;
+});
+
+process.on('exit', function() {
+ assert(stdoutEnd);
+ assert(gotExit);
+ assert(gotClose);
+});
--- /dev/null
+
+require('../index');
+
+var assert = require('assert'),
+ fork = require('child_process').fork;
+
+var cp = fork('worker-fork');
+
+var gotMessage = false,
+ gotExit = false,
+ gotClose = false;
+
+cp.on('message', function(msg) {
+ assert.strictEqual(msg, 'hello');
+ assert(!gotMessage);
+ assert(!gotClose);
+ gotMessage = true;
+});
+
+cp.on('exit', function(code, signal) {
+ assert.strictEqual(code, 0);
+ assert(!signal);
+ assert(!gotExit);
+ assert(!gotClose);
+ gotExit = true;
+});
+
+cp.on('close', function(code, signal) {
+ assert.strictEqual(code, 0);
+ assert(!signal);
+ assert(gotExit);
+ assert(gotMessage);
+ assert(!gotClose);
+ gotClose = true;
+});
+
+process.on('exit', function() {
+ assert(gotMessage);
+ assert(gotExit);
+ assert(gotClose);
+});
--- /dev/null
+
+require('../index');
+
+var assert = require('assert'),
+ spawn = require('child_process').spawn;
+ execFile = require('child_process').execFile;
+
+
+var cp1 = spawn(process.execPath, ['worker-spawn']);
+check(cp1);
+
+var cp2 = execFile(process.execPath, ['worker-spawn'], function(err) {
+ assert(!err);
+});
+check(cp2);
+
+
+function check(cp) {
+ var gotExit = false,
+ gotClose = false,
+ stdoutData = '',
+ stdoutEnd = false,
+ stderrData = '',
+ stderrEnd = false;
+
+ cp.stdout.setEncoding('ascii');
+
+ cp.stdout.on('data', function(data) {
+ assert(!stdoutEnd);
+ stdoutData += data;
+ });
+
+ cp.stdout.on('end', function(data) {
+ assert(!stdoutEnd)
+ assert.strictEqual(stdoutData.length, 100000);
+ stdoutEnd = true;
+ });
+
+ cp.stderr.setEncoding('ascii');
+
+ cp.stderr.on('data', function(data) {
+ stderrData += data;
+ });
+
+ cp.stderr.on('end', function(data) {
+ assert(!stderrEnd)
+ assert.strictEqual(stderrData.length, 100000);
+ stderrEnd = true;
+ });
+
+ cp.on('exit', function(code, signal) {
+ assert.strictEqual(code, 0);
+ assert(!signal);
+ assert(!gotExit);
+ assert(!gotClose);
+ gotExit = true;
+ });
+
+ cp.on('close', function(code, signal) {
+ assert.strictEqual(code, 0);
+ assert(!signal);
+ assert(!cp.stdout || stdoutEnd);
+ assert(!cp.stderr || stderrEnd);
+ assert(gotExit);
+ assert(!gotClose);
+ gotClose = true;
+ });
+
+ process.on('exit', function() {
+ assert(gotExit);
+ assert(gotClose);
+ });
+}
--- /dev/null
+
+var TESTS = [
+ 'test-spawn-and-execfile',
+ 'test-fork',
+ 'test-exec',
+];
+
+var execFile = require('child_process').execFile;
+var passed = 0, failed = 0;
+
+function next() {
+ var test = TESTS.shift();
+ if (!test)
+ done();
+
+ console.log("Running test: %s", test);
+ execFile(process.execPath, [test], { cwd: __dirname }, onExit);
+}
+
+function onExit(err, stdout, stderr) {
+ if (err) {
+ console.log("... failed:\n%s%s\n", stdout, stderr);
+ failed++;
+ } else {
+ console.log("... pass");
+ passed++;
+ }
+
+ next();
+}
+
+function done() {
+ console.log("Tests run: %d. Passed: %d. Failed: %d",
+ passed + failed,
+ passed,
+ failed);
+
+ process.exit(+(failed > 0));
+}
+
+next();
--- /dev/null
+
+process.send('hello');
+process.exit(0);
--- /dev/null
+
+var out = new Array(100000).join('x');
+
+console.log(out);
+console.error(out);
--- /dev/null
+editor
+======
+
+Launch $EDITOR in your program.
+
+example
+=======
+
+``` js
+var editor = require('editor');
+editor('beep.json', function (code, sig) {
+ console.log('finished editing with code ' + code);
+});
+```
+
+***
+
+```
+$ node edit.js
+```
+
+![editor](http://substack.net/images/screenshots/editor.png)
+
+```
+finished editing with code 0
+```
+
+methods
+=======
+
+``` js
+var editor = require('editor')
+```
+
+editor(file, opts={}, cb)
+-------------------------
+
+Launch the `$EDITOR` (or `opts.editor`) for `file`.
+
+When the editor exits, `cb(code, sig)` fires.
+
+install
+=======
+
+With [npm](http://npmjs.org) do:
+
+```
+npm install editor
+```
+
+license
+=======
+
+MIT
--- /dev/null
+{
+ "a" : 3,
+ "b" : 4,
+ "c" : 5
+}
--- /dev/null
+var editor = require('../');
+editor(__dirname + '/beep.json', function (code, sig) {
+ console.log('finished editing with code ' + code);
+});
--- /dev/null
+var spawn = require('child_process').spawn;
+
+module.exports = function (file, opts, cb) {
+ if (typeof opts === 'function') {
+ cb = opts;
+ opts = {};
+ }
+ if (!opts) opts = {};
+
+ var ed = /^win/.test(process.platform) ? 'notepad' : 'vim';
+ var editor = opts.editor || process.env.VISUAL || process.env.EDITOR || ed;
+
+ setRaw(true);
+ var ps = spawn(editor, [ file ], { customFds : [ 0, 1, 2 ] });
+
+ ps.on('exit', function (code, sig) {
+ setRaw(false);
+ process.stdin.pause();
+ if (typeof cb === 'function') cb(code, sig)
+ });
+};
+
+var tty = require('tty');
+function setRaw (mode) {
+ process.stdin.setRawMode ? process.stdin.setRawMode(mode) : tty.setRawMode(mode);
+}
--- /dev/null
+{
+ "name": "editor",
+ "version": "0.0.4",
+ "description": "launch $EDITOR in your program",
+ "main": "index.js",
+ "bin": {},
+ "directories": {
+ "example": "example",
+ "test": "test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/substack/node-editor.git"
+ },
+ "homepage": "https://github.com/substack/node-editor",
+ "keywords": [
+ "text",
+ "edit",
+ "shell"
+ ],
+ "author": {
+ "name": "James Halliday",
+ "email": "mail@substack.net",
+ "url": "http://substack.net"
+ },
+ "license": "MIT",
+ "engine": {
+ "node": ">=0.6"
+ },
+ "readme": "editor\n======\n\nLaunch $EDITOR in your program.\n\nexample\n=======\n\n``` js\nvar editor = require('editor');\neditor('beep.json', function (code, sig) {\n console.log('finished editing with code ' + code);\n});\n```\n\n***\n\n```\n$ node edit.js\n```\n\n![editor](http://substack.net/images/screenshots/editor.png)\n\n```\nfinished editing with code 0\n```\n\nmethods\n=======\n\n``` js\nvar editor = require('editor')\n```\n\neditor(file, opts={}, cb)\n-------------------------\n\nLaunch the `$EDITOR` (or `opts.editor`) for `file`.\n\nWhen the editor exits, `cb(code, sig)` fires.\n\ninstall\n=======\n\nWith [npm](http://npmjs.org) do:\n\n```\nnpm install editor\n```\n\nlicense\n=======\n\nMIT\n",
+ "readmeFilename": "README.markdown",
+ "bugs": {
+ "url": "https://github.com/substack/node-editor/issues"
+ },
+ "_id": "editor@0.0.4",
+ "_from": "editor@"
+}
# Glob
+Match files using the patterns the shell uses, like stars and stuff.
+
This is a glob implementation in JavaScript. It uses the `minimatch`
library to do its matching.
Perform an asynchronous glob search.
-## glob.sync(pattern, [options]
+## glob.sync(pattern, [options])
* `pattern` {String} Pattern to be matched
* `options` {Object}
* `aborted` Boolean which is set to true when calling `abort()`. There
is no way at this time to continue a glob search after aborting, but
you can re-use the statCache to avoid having to duplicate syscalls.
+* `statCache` Collection of all the stat results the glob search
+ performed.
+* `cache` Convenience object. Each field has the following possible
+ values:
+ * `false` - Path does not exist
+ * `true` - Path exists
+ * `1` - Path exists, and is not a directory
+ * `2` - Path exists, and is a directory
+ * `[file, entries, ...]` - Path exists, is a directory, and the
+ array value is the results of `fs.readdir`
### Events
* `root` The place where patterns starting with `/` will be mounted
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
systems, and `C:\` or some such on Windows.)
+* `dot` Include `.dot` files in normal matches and `globstar` matches.
+ Note that an explicit dot in a portion of the pattern will always
+ match dot files.
* `nomount` By default, a pattern starting with a forward-slash will be
"mounted" onto the root setting, so that a valid filesystem path is
returned. Set this flag to disable that behavior.
when attempting to read a directory, the process will just continue on
in search of other matches. Set the `strict` option to raise an error
in these cases.
+* `cache` See `cache` property above. Pass in a previously generated
+ cache object to save some fs calls.
* `statCache` A cache of results of filesystem information, to prevent
unnecessary stat calls. While it should not normally be necessary to
set this, you may pass the statCache from one glob() call to the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
-`a/**b` will not. **Note that this is different from the way that `**` is
-handled by ruby's `Dir` class.**
+`a/**b` will not.
If an escaped pattern has no matches, and the `nonull` flag is set,
then glob returns the pattern as-provided, rather than
As part of its internal implementation, this program caches all stat
and readdir calls that it makes, in order to cut down on system
overhead. However, this also makes it even more susceptible to races,
-especially if the statCache object is reused between glob calls.
+especially if the cache or statCache objects are reused between glob
+calls.
-Users are thus advised not to use a glob result as a
-guarantee of filesystem state in the face of rapid changes.
-For the vast majority of operations, this is never a problem.
+Users are thus advised not to use a glob result as a guarantee of
+filesystem state in the face of rapid changes. For the vast majority
+of operations, this is never a problem.
this.maxDepth = options.maxDepth || 1000
this.maxLength = options.maxLength || Infinity
+ this.cache = options.cache || {}
this.statCache = options.statCache || {}
this.changedCwd = false
this.error = null
this.aborted = false
+ // list of all the patterns that ** has resolved do, so
+ // we can avoid visiting multiple times.
+ this._globstars = {}
+
EE.call(this)
// process each pattern in the minimatch set
if (this.mark) {
// at *some* point we statted all of these
all = all.map(function (m) {
- var sc = this.statCache[m]
+ var sc = this.cache[m]
if (!sc)
return m
var isDir = (Array.isArray(sc) || sc === 2)
}
Glob.prototype.emitMatch = function (m) {
- this._emitQueue.push(m)
- this._processEmitQueue()
+ if (!this.stat || this.statCache[m] || m === this.EOF) {
+ this._emitQueue.push(m)
+ this._processEmitQueue()
+ } else {
+ this._stat(m, function(exists, isDir) {
+ if (exists) {
+ this._emitQueue.push(m)
+ this._processEmitQueue()
+ }
+ })
+ }
}
Glob.prototype._processEmitQueue = function (m) {
// nothing more to do, either way.
if (exists) {
if (prefix && isAbsolute(prefix) && !this.nomount) {
- if (prefix.charAt(0) === "/") {
+ if (prefix.charAt(0) === "/") {
prefix = path.join(this.root, prefix)
- } else {
- prefix = path.resolve(this.root, prefix)
- }
+ } else {
+ prefix = path.resolve(this.root, prefix)
+ }
}
if (process.platform === "win32")
s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n)))
}, this)
+ s = s.filter(function (pattern) {
+ var key = gsKey(pattern)
+ var seen = !this._globstars[key]
+ this._globstars[key] = true
+ return seen
+ }, this)
+
+ if (!s.length)
+ return cb()
+
// now asyncForEach over this
var l = s.length
, errState = null
// It will only match dot entries if it starts with a dot, or if
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
var pn = pattern[n]
- if (typeof pn === "string") {
- var found = entries.indexOf(pn) !== -1
- entries = found ? entries[pn] : []
- } else {
- var rawGlob = pattern[n]._glob
- , dotOk = this.dot || rawGlob.charAt(0) === "."
+ var rawGlob = pattern[n]._glob
+ , dotOk = this.dot || rawGlob.charAt(0) === "."
- entries = entries.filter(function (e) {
- return (e.charAt(0) !== "." || dotOk) &&
- (typeof pattern[n] === "string" && e === pattern[n] ||
- e.match(pattern[n]))
- })
- }
+ entries = entries.filter(function (e) {
+ return (e.charAt(0) !== "." || dotOk) &&
+ e.match(pattern[n])
+ })
// If n === pattern.length - 1, then there's no need for the extra stat
// *unless* the user has specified "mark" or "stat" explicitly.
}
+function gsKey (pattern) {
+ return '**' + pattern.map(function (p) {
+ return (p === minimatch.GLOBSTAR) ? '**' : (''+p)
+ }).join('/')
+}
+
Glob.prototype._stat = function (f, cb) {
assert(this instanceof Glob)
var abs = f
} else if (this.changedCwd) {
abs = path.resolve(this.cwd, f)
}
- this.log('stat', [this.cwd, f, '=', abs])
+
if (f.length > this.maxLength) {
var er = new Error("Path name too long")
er.code = "ENAMETOOLONG"
return this._afterStat(f, abs, cb, er)
}
- if (this.statCache.hasOwnProperty(f)) {
- var exists = this.statCache[f]
+ this.log('stat', [this.cwd, f, '=', abs])
+
+ if (!this.stat && this.cache.hasOwnProperty(f)) {
+ var exists = this.cache[f]
, isDir = exists && (Array.isArray(exists) || exists === 2)
if (this.sync) return cb.call(this, !!exists, isDir)
return process.nextTick(cb.bind(this, !!exists, isDir))
}
- if (this.sync) {
- var er, stat
+ var stat = this.statCache[abs]
+ if (this.sync || stat) {
+ var er
try {
stat = fs.statSync(abs)
} catch (e) {
stat = null
}
+ var emit = !this.statCache[abs]
+ this.statCache[abs] = stat
+
if (er || !stat) {
exists = false
} else {
exists = stat.isDirectory() ? 2 : 1
+ if (emit)
+ this.emit('stat', f, stat)
}
- this.statCache[f] = this.statCache[f] || exists
+ this.cache[f] = this.cache[f] || exists
cb.call(this, !!exists, exists === 2)
}
abs = path.resolve(this.cwd, f)
}
- this.log('readdir', [this.cwd, f, abs])
if (f.length > this.maxLength) {
var er = new Error("Path name too long")
er.code = "ENAMETOOLONG"
return this._afterReaddir(f, abs, cb, er)
}
- if (this.statCache.hasOwnProperty(f)) {
- var c = this.statCache[f]
+ this.log('readdir', [this.cwd, f, abs])
+ if (this.cache.hasOwnProperty(f)) {
+ var c = this.cache[f]
if (Array.isArray(c)) {
if (this.sync) return cb.call(this, null, c)
return process.nextTick(cb.bind(this, null, c))
Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) {
assert(this instanceof Glob)
if (entries && !er) {
- this.statCache[f] = entries
+ this.cache[f] = entries
// if we haven't asked to stat everything for suresies, then just
// assume that everything in there exists, so we can avoid
// having to stat it a second time. This also gets us one step
entries.forEach(function (e) {
if (f === "/") e = f + e
else e = f + "/" + e
- this.statCache[e] = true
+ this.cache[e] = true
}, this)
}
// now handle errors, and cache the information
if (er) switch (er.code) {
case "ENOTDIR": // totally normal. means it *does* exist.
- this.statCache[f] = 1
+ this.cache[f] = 1
return cb.call(this, er)
case "ENOENT": // not terribly unusual
case "ELOOP":
case "ENAMETOOLONG":
case "UNKNOWN":
- this.statCache[f] = false
+ this.cache[f] = false
return cb.call(this, er)
default: // some unusual error. Treat as failure.
- this.statCache[f] = false
+ this.cache[f] = false
if (this.strict) this.emit("error", er)
if (!this.silent) console.error("glob error", er)
return cb.call(this, er)
},
"name": "glob",
"description": "a little globber",
- "version": "3.1.21",
+ "version": "3.2.1",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-glob.git"
"test": "tap test/*.js"
},
"license": "BSD",
- "readme": "# Glob\n\nThis is a glob implementation in JavaScript. It uses the `minimatch`\nlibrary to do its matching.\n\n## Attention: node-glob users!\n\nThe API has changed dramatically between 2.x and 3.x. This library is\nnow 100% JavaScript, and the integer flags have been replaced with an\noptions object.\n\nAlso, there's an event emitter class, proper tests, and all the other\nthings you've come to expect from node modules.\n\nAnd best of all, no compilation!\n\n## Usage\n\n```javascript\nvar glob = require(\"glob\")\n\n// options is optional\nglob(\"**/*.js\", options, function (er, files) {\n // files is an array of filenames.\n // If the `nonull` option is set, and nothing\n // was found, then files is [\"**/*.js\"]\n // er is an error object or null.\n})\n```\n\n## Features\n\nPlease see the [minimatch\ndocumentation](https://github.com/isaacs/minimatch) for more details.\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n* [minimatch documentation](https://github.com/isaacs/minimatch)\n\n## glob(pattern, [options], cb)\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* `cb` {Function}\n * `err` {Error | null}\n * `matches` {Array<String>} filenames found matching the pattern\n\nPerform an asynchronous glob search.\n\n## glob.sync(pattern, [options]\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* return: {Array<String>} filenames found matching the pattern\n\nPerform a synchronous glob search.\n\n## Class: glob.Glob\n\nCreate a Glob object by instanting the `glob.Glob` class.\n\n```javascript\nvar Glob = require(\"glob\").Glob\nvar mg = new Glob(pattern, options, cb)\n```\n\nIt's an EventEmitter, and starts walking the filesystem to find matches\nimmediately.\n\n### new glob.Glob(pattern, [options], [cb])\n\n* `pattern` {String} pattern to search for\n* `options` {Object}\n* `cb` {Function} Called when an error occurs, or matches are found\n * `err` {Error | null}\n * `matches` {Array<String>} filenames found matching the pattern\n\nNote that if the `sync` flag is set in the options, then matches will\nbe immediately available on the `g.found` member.\n\n### Properties\n\n* `minimatch` The minimatch object that the glob uses.\n* `options` The options object passed in.\n* `error` The error encountered. When an error is encountered, the\n glob object is in an undefined state, and should be discarded.\n* `aborted` Boolean which is set to true when calling `abort()`. There\n is no way at this time to continue a glob search after aborting, but\n you can re-use the statCache to avoid having to duplicate syscalls.\n\n### Events\n\n* `end` When the matching is finished, this is emitted with all the\n matches found. If the `nonull` option is set, and no match was found,\n then the `matches` list contains the original pattern. The matches\n are sorted, unless the `nosort` flag is set.\n* `match` Every time a match is found, this is emitted with the matched.\n* `error` Emitted when an unexpected error is encountered, or whenever\n any fs error occurs if `options.strict` is set.\n* `abort` When `abort()` is called, this event is raised.\n\n### Methods\n\n* `abort` Stop the search.\n\n### Options\n\nAll the options that can be passed to Minimatch can also be passed to\nGlob to change pattern matching behavior. Also, some have been added,\nor have glob-specific ramifications.\n\nAll options are false by default, unless otherwise noted.\n\nAll options are added to the glob object, as well.\n\n* `cwd` The current working directory in which to search. Defaults\n to `process.cwd()`.\n* `root` The place where patterns starting with `/` will be mounted\n onto. Defaults to `path.resolve(options.cwd, \"/\")` (`/` on Unix\n systems, and `C:\\` or some such on Windows.)\n* `nomount` By default, a pattern starting with a forward-slash will be\n \"mounted\" onto the root setting, so that a valid filesystem path is\n returned. Set this flag to disable that behavior.\n* `mark` Add a `/` character to directory matches. Note that this\n requires additional stat calls.\n* `nosort` Don't sort the results.\n* `stat` Set to true to stat *all* results. This reduces performance\n somewhat, and is completely unnecessary, unless `readdir` is presumed\n to be an untrustworthy indicator of file existence. It will cause\n ELOOP to be triggered one level sooner in the case of cyclical\n symbolic links.\n* `silent` When an unusual error is encountered\n when attempting to read a directory, a warning will be printed to\n stderr. Set the `silent` option to true to suppress these warnings.\n* `strict` When an unusual error is encountered\n when attempting to read a directory, the process will just continue on\n in search of other matches. Set the `strict` option to raise an error\n in these cases.\n* `statCache` A cache of results of filesystem information, to prevent\n unnecessary stat calls. While it should not normally be necessary to\n set this, you may pass the statCache from one glob() call to the\n options object of another, if you know that the filesystem will not\n change between calls. (See \"Race Conditions\" below.)\n* `sync` Perform a synchronous glob search.\n* `nounique` In some cases, brace-expanded patterns can result in the\n same file showing up multiple times in the result set. By default,\n this implementation prevents duplicates in the result set.\n Set this flag to disable that behavior.\n* `nonull` Set to never return an empty set, instead returning a set\n containing the pattern itself. This is the default in glob(3).\n* `nocase` Perform a case-insensitive match. Note that case-insensitive\n filesystems will sometimes result in glob returning results that are\n case-insensitively matched anyway, since readdir and stat will not\n raise an error.\n* `debug` Set to enable debug logging in minimatch and glob.\n* `globDebug` Set to enable debug logging in glob, but not minimatch.\n\n## Comparisons to other fnmatch/glob implementations\n\nWhile strict compliance with the existing standards is a worthwhile\ngoal, some discrepancies exist between node-glob and other\nimplementations, and are intentional.\n\nIf the pattern starts with a `!` character, then it is negated. Set the\n`nonegate` flag to suppress this behavior, and treat leading `!`\ncharacters normally. This is perhaps relevant if you wish to start the\npattern with a negative extglob pattern like `!(a|B)`. Multiple `!`\ncharacters at the start of a pattern will negate the pattern multiple\ntimes.\n\nIf a pattern starts with `#`, then it is treated as a comment, and\nwill not match anything. Use `\\#` to match a literal `#` at the\nstart of a line, or set the `nocomment` flag to suppress this behavior.\n\nThe double-star character `**` is supported by default, unless the\n`noglobstar` flag is set. This is supported in the manner of bsdglob\nand bash 4.1, where `**` only has special significance if it is the only\nthing in a path part. That is, `a/**/b` will match `a/x/y/b`, but\n`a/**b` will not. **Note that this is different from the way that `**` is\nhandled by ruby's `Dir` class.**\n\nIf an escaped pattern has no matches, and the `nonull` flag is set,\nthen glob returns the pattern as-provided, rather than\ninterpreting the character escapes. For example,\n`glob.match([], \"\\\\*a\\\\?\")` will return `\"\\\\*a\\\\?\"` rather than\n`\"*a?\"`. This is akin to setting the `nullglob` option in bash, except\nthat it does not resolve escaped pattern characters.\n\nIf brace expansion is not disabled, then it is performed before any\nother interpretation of the glob pattern. Thus, a pattern like\n`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded\n**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are\nchecked for validity. Since those two are valid, matching proceeds.\n\n## Windows\n\n**Please only use forward-slashes in glob expressions.**\n\nThough windows uses either `/` or `\\` as its path separator, only `/`\ncharacters are used by this glob implementation. You must use\nforward-slashes **only** in glob expressions. Back-slashes will always\nbe interpreted as escape characters, not path separators.\n\nResults from absolute patterns such as `/foo/*` are mounted onto the\nroot setting using `path.join`. On windows, this will by default result\nin `/foo/*` matching `C:\\foo\\bar.txt`.\n\n## Race Conditions\n\nGlob searching, by its very nature, is susceptible to race conditions,\nsince it relies on directory walking and such.\n\nAs a result, it is possible that a file that exists when glob looks for\nit may have been deleted or modified by the time it returns the result.\n\nAs part of its internal implementation, this program caches all stat\nand readdir calls that it makes, in order to cut down on system\noverhead. However, this also makes it even more susceptible to races,\nespecially if the statCache object is reused between glob calls.\n\nUsers are thus advised not to use a glob result as a\nguarantee of filesystem state in the face of rapid changes.\nFor the vast majority of operations, this is never a problem.\n",
+ "readme": "# Glob\n\nMatch files using the patterns the shell uses, like stars and stuff.\n\nThis is a glob implementation in JavaScript. It uses the `minimatch`\nlibrary to do its matching.\n\n## Attention: node-glob users!\n\nThe API has changed dramatically between 2.x and 3.x. This library is\nnow 100% JavaScript, and the integer flags have been replaced with an\noptions object.\n\nAlso, there's an event emitter class, proper tests, and all the other\nthings you've come to expect from node modules.\n\nAnd best of all, no compilation!\n\n## Usage\n\n```javascript\nvar glob = require(\"glob\")\n\n// options is optional\nglob(\"**/*.js\", options, function (er, files) {\n // files is an array of filenames.\n // If the `nonull` option is set, and nothing\n // was found, then files is [\"**/*.js\"]\n // er is an error object or null.\n})\n```\n\n## Features\n\nPlease see the [minimatch\ndocumentation](https://github.com/isaacs/minimatch) for more details.\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n* [minimatch documentation](https://github.com/isaacs/minimatch)\n\n## glob(pattern, [options], cb)\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* `cb` {Function}\n * `err` {Error | null}\n * `matches` {Array<String>} filenames found matching the pattern\n\nPerform an asynchronous glob search.\n\n## glob.sync(pattern, [options])\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* return: {Array<String>} filenames found matching the pattern\n\nPerform a synchronous glob search.\n\n## Class: glob.Glob\n\nCreate a Glob object by instanting the `glob.Glob` class.\n\n```javascript\nvar Glob = require(\"glob\").Glob\nvar mg = new Glob(pattern, options, cb)\n```\n\nIt's an EventEmitter, and starts walking the filesystem to find matches\nimmediately.\n\n### new glob.Glob(pattern, [options], [cb])\n\n* `pattern` {String} pattern to search for\n* `options` {Object}\n* `cb` {Function} Called when an error occurs, or matches are found\n * `err` {Error | null}\n * `matches` {Array<String>} filenames found matching the pattern\n\nNote that if the `sync` flag is set in the options, then matches will\nbe immediately available on the `g.found` member.\n\n### Properties\n\n* `minimatch` The minimatch object that the glob uses.\n* `options` The options object passed in.\n* `error` The error encountered. When an error is encountered, the\n glob object is in an undefined state, and should be discarded.\n* `aborted` Boolean which is set to true when calling `abort()`. There\n is no way at this time to continue a glob search after aborting, but\n you can re-use the statCache to avoid having to duplicate syscalls.\n* `statCache` Collection of all the stat results the glob search\n performed.\n* `cache` Convenience object. Each field has the following possible\n values:\n * `false` - Path does not exist\n * `true` - Path exists\n * `1` - Path exists, and is not a directory\n * `2` - Path exists, and is a directory\n * `[file, entries, ...]` - Path exists, is a directory, and the\n array value is the results of `fs.readdir`\n\n### Events\n\n* `end` When the matching is finished, this is emitted with all the\n matches found. If the `nonull` option is set, and no match was found,\n then the `matches` list contains the original pattern. The matches\n are sorted, unless the `nosort` flag is set.\n* `match` Every time a match is found, this is emitted with the matched.\n* `error` Emitted when an unexpected error is encountered, or whenever\n any fs error occurs if `options.strict` is set.\n* `abort` When `abort()` is called, this event is raised.\n\n### Methods\n\n* `abort` Stop the search.\n\n### Options\n\nAll the options that can be passed to Minimatch can also be passed to\nGlob to change pattern matching behavior. Also, some have been added,\nor have glob-specific ramifications.\n\nAll options are false by default, unless otherwise noted.\n\nAll options are added to the glob object, as well.\n\n* `cwd` The current working directory in which to search. Defaults\n to `process.cwd()`.\n* `root` The place where patterns starting with `/` will be mounted\n onto. Defaults to `path.resolve(options.cwd, \"/\")` (`/` on Unix\n systems, and `C:\\` or some such on Windows.)\n* `dot` Include `.dot` files in normal matches and `globstar` matches.\n Note that an explicit dot in a portion of the pattern will always\n match dot files.\n* `nomount` By default, a pattern starting with a forward-slash will be\n \"mounted\" onto the root setting, so that a valid filesystem path is\n returned. Set this flag to disable that behavior.\n* `mark` Add a `/` character to directory matches. Note that this\n requires additional stat calls.\n* `nosort` Don't sort the results.\n* `stat` Set to true to stat *all* results. This reduces performance\n somewhat, and is completely unnecessary, unless `readdir` is presumed\n to be an untrustworthy indicator of file existence. It will cause\n ELOOP to be triggered one level sooner in the case of cyclical\n symbolic links.\n* `silent` When an unusual error is encountered\n when attempting to read a directory, a warning will be printed to\n stderr. Set the `silent` option to true to suppress these warnings.\n* `strict` When an unusual error is encountered\n when attempting to read a directory, the process will just continue on\n in search of other matches. Set the `strict` option to raise an error\n in these cases.\n* `cache` See `cache` property above. Pass in a previously generated\n cache object to save some fs calls.\n* `statCache` A cache of results of filesystem information, to prevent\n unnecessary stat calls. While it should not normally be necessary to\n set this, you may pass the statCache from one glob() call to the\n options object of another, if you know that the filesystem will not\n change between calls. (See \"Race Conditions\" below.)\n* `sync` Perform a synchronous glob search.\n* `nounique` In some cases, brace-expanded patterns can result in the\n same file showing up multiple times in the result set. By default,\n this implementation prevents duplicates in the result set.\n Set this flag to disable that behavior.\n* `nonull` Set to never return an empty set, instead returning a set\n containing the pattern itself. This is the default in glob(3).\n* `nocase` Perform a case-insensitive match. Note that case-insensitive\n filesystems will sometimes result in glob returning results that are\n case-insensitively matched anyway, since readdir and stat will not\n raise an error.\n* `debug` Set to enable debug logging in minimatch and glob.\n* `globDebug` Set to enable debug logging in glob, but not minimatch.\n\n## Comparisons to other fnmatch/glob implementations\n\nWhile strict compliance with the existing standards is a worthwhile\ngoal, some discrepancies exist between node-glob and other\nimplementations, and are intentional.\n\nIf the pattern starts with a `!` character, then it is negated. Set the\n`nonegate` flag to suppress this behavior, and treat leading `!`\ncharacters normally. This is perhaps relevant if you wish to start the\npattern with a negative extglob pattern like `!(a|B)`. Multiple `!`\ncharacters at the start of a pattern will negate the pattern multiple\ntimes.\n\nIf a pattern starts with `#`, then it is treated as a comment, and\nwill not match anything. Use `\\#` to match a literal `#` at the\nstart of a line, or set the `nocomment` flag to suppress this behavior.\n\nThe double-star character `**` is supported by default, unless the\n`noglobstar` flag is set. This is supported in the manner of bsdglob\nand bash 4.1, where `**` only has special significance if it is the only\nthing in a path part. That is, `a/**/b` will match `a/x/y/b`, but\n`a/**b` will not.\n\nIf an escaped pattern has no matches, and the `nonull` flag is set,\nthen glob returns the pattern as-provided, rather than\ninterpreting the character escapes. For example,\n`glob.match([], \"\\\\*a\\\\?\")` will return `\"\\\\*a\\\\?\"` rather than\n`\"*a?\"`. This is akin to setting the `nullglob` option in bash, except\nthat it does not resolve escaped pattern characters.\n\nIf brace expansion is not disabled, then it is performed before any\nother interpretation of the glob pattern. Thus, a pattern like\n`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded\n**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are\nchecked for validity. Since those two are valid, matching proceeds.\n\n## Windows\n\n**Please only use forward-slashes in glob expressions.**\n\nThough windows uses either `/` or `\\` as its path separator, only `/`\ncharacters are used by this glob implementation. You must use\nforward-slashes **only** in glob expressions. Back-slashes will always\nbe interpreted as escape characters, not path separators.\n\nResults from absolute patterns such as `/foo/*` are mounted onto the\nroot setting using `path.join`. On windows, this will by default result\nin `/foo/*` matching `C:\\foo\\bar.txt`.\n\n## Race Conditions\n\nGlob searching, by its very nature, is susceptible to race conditions,\nsince it relies on directory walking and such.\n\nAs a result, it is possible that a file that exists when glob looks for\nit may have been deleted or modified by the time it returns the result.\n\nAs part of its internal implementation, this program caches all stat\nand readdir calls that it makes, in order to cut down on system\noverhead. However, this also makes it even more susceptible to races,\nespecially if the cache or statCache objects are reused between glob\ncalls.\n\nUsers are thus advised not to use a glob result as a guarantee of\nfilesystem state in the face of rapid changes. For the vast majority\nof operations, this is never a problem.\n",
"readmeFilename": "README.md",
- "_id": "glob@3.1.21",
- "_from": "glob@latest"
+ "bugs": {
+ "url": "https://github.com/isaacs/node-glob/issues"
+ },
+ "_id": "glob@3.2.1",
+ "dist": {
+ "shasum": "57af70ec73ba2323bfe3f29a067765db64c5d758"
+ },
+ "_from": "glob@3.2.1",
+ "_resolved": "https://registry.npmjs.org/glob/-/glob-3.2.1.tgz"
}
"./test/bash-comparison.js",
"./test/bash-results.json",
"./test/cwd-test.js",
+ "./test/globstar-match.js",
"./test/mark.js",
"./test/nocase-nomagic.js",
"./test/pause-resume.js",
"./test/root-nomount.js",
"./test/root.js",
+ "./test/stat.js",
"./test/zz-cleanup.js",
"/tmp/glob-test/asdf",
"/tmp/glob-test/bar",
--- /dev/null
+var Glob = require("../glob.js").Glob
+var test = require('tap').test
+
+test('globstar should not have dupe matches', function(t) {
+ var pattern = 'a/**/[gh]'
+ var g = new Glob(pattern, { cwd: __dirname })
+ var matches = []
+ g.on('match', function(m) {
+ console.error('match %j', m)
+ matches.push(m)
+ })
+ g.on('end', function(set) {
+ console.error('set', set)
+ matches = matches.sort()
+ set = set.sort()
+ t.same(matches, set, 'should have same set of matches')
+ t.end()
+ })
+})
--- /dev/null
+var glob = require('../')
+var test = require('tap').test
+var path = require('path')
+
+test('stat all the things', function(t) {
+ var g = new glob.Glob('a/*abc*/**', { stat: true, cwd: __dirname })
+ var matches = []
+ g.on('match', function(m) {
+ matches.push(m)
+ })
+ var stats = []
+ g.on('stat', function(m) {
+ stats.push(m)
+ })
+ g.on('end', function(eof) {
+ stats = stats.sort()
+ matches = matches.sort()
+ eof = eof.sort()
+ t.same(stats, matches)
+ t.same(eof, matches)
+ var cache = Object.keys(this.statCache)
+ t.same(cache.map(function (f) {
+ return path.relative(__dirname, f)
+ }).sort(), matches)
+
+ cache.forEach(function(c) {
+ t.equal(typeof this.statCache[c], 'object')
+ }, this)
+
+ t.end()
+ })
+})
-Just like node's `fs` module, but it does an incremental back-off when
-EMFILE is encountered.
+# graceful-fs
-Useful in asynchronous situations where one needs to try to open lots
-and lots of files.
+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
+
+graceful-fs:
+
+* keeps track of how many file descriptors are open, and by default
+ limits this to 1024. Any further requests to open a file are put in a
+ queue until new slots become available. If 1024 turns out to be too
+ much, it decreases the limit further.
+* 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.
+
+## Configuration
+
+The maximum number of open file descriptors that graceful-fs manages may
+be adjusted by setting `fs.MAX_OPEN` to a different number. The default
+is 1024.
"url": "http://blog.izs.me"
},
"name": "graceful-fs",
- "description": "fs monkey-patching to avoid EMFILE and other problems",
- "version": "1.2.0",
+ "description": "A drop-in replacement for fs, making various improvements.",
+ "version": "1.2.1",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-graceful-fs.git"
},
"keywords": [
"fs",
- "EMFILE",
+ "module",
+ "reading",
+ "retry",
+ "retries",
+ "queue",
"error",
+ "errors",
"handling",
- "monkeypatch"
+ "EMFILE",
+ "EAGAIN",
+ "EINVAL",
+ "EPERM",
+ "EACCESS"
],
"license": "BSD",
- "readme": "Just like node's `fs` module, but it does an incremental back-off when\nEMFILE is encountered.\n\nUseful in asynchronous situations where one needs to try to open lots\nand lots of files.\n",
+ "readme": "# graceful-fs\n\ngraceful-fs functions as a drop-in replacement for the fs module,\nmaking various improvements.\n\nThe improvements are meant to normalize behavior across different\nplatforms and environments, and to make filesystem access more\nresilient to errors.\n\n## Improvements over fs module\n\ngraceful-fs:\n\n* keeps track of how many file descriptors are open, and by default\n limits this to 1024. Any further requests to open a file are put in a\n queue until new slots become available. If 1024 turns out to be too\n much, it decreases the limit further.\n* fixes `lchmod` for Node versions prior to 0.6.2.\n* implements `fs.lutimes` if possible. Otherwise it becomes a noop.\n* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or\n `lchown` if the user isn't root.\n* makes `lchmod` and `lchown` become noops, if not available.\n* retries reading a file if `read` results in EAGAIN error.\n\nOn Windows, it retries renaming a file for up to one second if `EACCESS`\nor `EPERM` error occurs, likely because antivirus software has locked\nthe directory.\n\n## Configuration\n\nThe maximum number of open file descriptors that graceful-fs manages may\nbe adjusted by setting `fs.MAX_OPEN` to a different number. The default\nis 1024.\n",
"readmeFilename": "README.md",
- "_id": "graceful-fs@1.2.0",
- "_from": "graceful-fs@latest"
+ "bugs": {
+ "url": "https://github.com/isaacs/node-graceful-fs/issues"
+ },
+ "_id": "graceful-fs@1.2.1",
+ "_from": "graceful-fs@~1.2.0"
}
if (er) return cb()
var deps = {}
var n = dir.length
+ if (n === 0) return cb(null, deps)
dir.forEach(function (d) {
if (d.match(/^\./)) return next()
if (test !== isTestPkg(d))
}
if (!package.author) {
- exports.author = config['init.author.name']
+ exports.author = config.get('init.author.name')
? {
- "name" : config['init.author.name'],
- "email" : config['init.author.email'],
- "url" : config['init.author.url']
+ "name" : config.get('init.author.name'),
+ "email" : config.get('init.author.email'),
+ "url" : config.get('init.author.url')
}
: prompt('author')
}
function init (dir, input, config, cb) {
if (typeof config === 'function')
cb = config, config = {}
+
+ // accept either a plain-jane object, or a config object
+ // with a "get" method.
+ if (typeof config.get !== 'function') {
+ var data = config
+ config = {
+ get: function (k) {
+ return data[k]
+ },
+ toJSON: function () {
+ return data
+ }
+ }
+ }
+
var package = path.resolve(dir, 'package.json')
input = path.resolve(input)
var pkg
},
"readme": "# promzard\n\nA prompting wizard for building files from specialized PromZard modules.\nUsed by `npm init`.\n\nA reimplementation of @SubStack's\n[prompter](https://github.com/substack/node-prompter), which does not\nuse AST traversal.\n\nFrom another point of view, it's a reimplementation of\n[@Marak](https://github.com/marak)'s\n[wizard](https://github.com/Marak/wizard) which doesn't use schemas.\n\nThe goal is a nice drop-in enhancement for `npm init`.\n\n## Usage\n\n```javascript\nvar promzard = require('promzard')\npromzard(inputFile, optionalContextAdditions, function (er, data) {\n // .. you know what you doing ..\n})\n```\n\nIn the `inputFile` you can have something like this:\n\n```javascript\nvar fs = require('fs')\nmodule.exports = {\n \"greeting\": prompt(\"Who shall you greet?\", \"world\", function (who) {\n return \"Hello, \" + who\n }),\n \"filename\": __filename,\n \"directory\": function (cb) {\n fs.readdir(__dirname, cb)\n }\n}\n```\n\nWhen run, promzard will display the prompts and resolve the async\nfunctions in order, and then either give you an error, or the resolved\ndata, ready to be dropped into a JSON file or some other place.\n\n\n### promzard(inputFile, ctx, callback)\n\nThe inputFile is just a node module. You can require() things, set\nmodule.exports, etc. Whatever that module exports is the result, and it\nis walked over to call any functions as described below.\n\nThe only caveat is that you must give PromZard the full absolute path\nto the module (you can get this via Node's `require.resolve`.) Also,\nthe `prompt` function is injected into the context object, so watch out.\n\nWhatever you put in that `ctx` will of course also be available in the\nmodule. You can get quite fancy with this, passing in existing configs\nand so on.\n\n### Class: promzard.PromZard(file, ctx)\n\nJust like the `promzard` function, but the EventEmitter that makes it\nall happen. Emits either a `data` event with the data, or a `error`\nevent if it blows up.\n\nIf `error` is emitted, then `data` never will be.\n\n### prompt(...)\n\nIn the promzard input module, you can call the `prompt` function.\nThis prompts the user to input some data. The arguments are interpreted\nbased on type:\n\n1. `string` The first string encountered is the prompt. The second is\n the default value.\n2. `function` A transformer function which receives the data and returns\n something else. More than meets the eye.\n3. `object` The `prompt` member is the prompt, the `default` member is\n the default value, and the `transform` is the transformer.\n\nWhatever the final value is, that's what will be put on the resulting\nobject.\n\n### Functions\n\nIf there are any functions on the promzard input module's exports, then\npromzard will call each of them with a callback. This way, your module\ncan do asynchronous actions if necessary to validate or ascertain\nwhatever needs verification.\n\nThe functions are called in the context of the ctx object, and are given\na single argument, which is a callback that should be called with either\nan error, or the result to assign to that spot.\n\nIn the async function, you can also call prompt() and return the result\nof the prompt in the callback.\n\nFor example, this works fine in a promzard module:\n\n```\nexports.asyncPrompt = function (cb) {\n fs.stat(someFile, function (er, st) {\n // if there's an error, no prompt, just error\n // otherwise prompt and use the actual file size as the default\n cb(er, prompt('file size', st.size))\n })\n}\n```\n\nYou can also return other async functions in the async function\ncallback. Though that's a bit silly, it could be a handy way to reuse\nfunctionality in some cases.\n\n### Sync vs Async\n\nThe `prompt()` function is not synchronous, though it appears that way.\nIt just returns a token that is swapped out when the data object is\nwalked over asynchronously later, and returns a token.\n\nFor that reason, prompt() calls whose results don't end up on the data\nobject are never shown to the user. For example, this will only prompt\nonce:\n\n```\nexports.promptThreeTimes = prompt('prompt me once', 'shame on you')\nexports.promptThreeTimes = prompt('prompt me twice', 'um....')\nexports.promptThreeTimes = prompt('you cant prompt me again')\n```\n\n### Isn't this exactly the sort of 'looks sync' that you said was bad about other libraries?\n\nYeah, sorta. I wouldn't use promzard for anything more complicated than\na wizard that spits out prompts to set up a config file or something.\nMaybe there are other use cases I haven't considered.\n",
"readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/isaacs/promzard/issues"
+ },
"_id": "promzard@0.2.0",
"_from": "promzard@~0.2.0"
}
{
"name": "init-package-json",
- "version": "0.0.6",
+ "version": "0.0.8",
"main": "init-package-json.js",
"scripts": {
"test": "tap test/*.js"
],
"readme": "# init-package-json\n\nA node module to get your node module started.\n\n## Usage\n\n```javascript\nvar init = require('init-package-json')\nvar path = require('path')\n\n// a path to a promzard module. In the event that this file is\n// not found, one will be provided for you.\nvar initFile = path.resolve(process.env.HOME, '.npm-init')\n\n// the dir where we're doin stuff.\nvar dir = process.cwd()\n\n// extra stuff that gets put into the PromZard module's context.\n// In npm, this is the resolved config object. Exposed as 'config'\n// Optional.\nvar configData = { some: 'extra stuff' }\n\n// Any existing stuff from the package.json file is also exposed in the\n// PromZard module as the `package` object. There will also be free\n// vars for:\n// * `filename` path to the package.json file\n// * `basename` the tip of the package dir\n// * `dirname` the parent of the package dir\n\ninit(dir, initFile, configData, function (er, data) {\n // the data's already been written to {dir}/package.json\n // now you can do stuff with it\n})\n```\n\nOr from the command line:\n\n```\n$ npm-init\n```\n\nSee [PromZard](https://github.com/isaacs/promzard) for details about\nwhat can go in the config file.\n",
"readmeFilename": "README.md",
- "_id": "init-package-json@0.0.6",
- "dist": {
- "shasum": "3d16465b07d401b8ec2a5df1bd379dccc2dd6a37"
+ "bugs": {
+ "url": "https://github.com/isaacs/init-package-json/issues"
},
- "_from": "init-package-json@0.0.6"
+ "_id": "init-package-json@0.0.8",
+ "_from": "init-package-json@latest"
}
if (er.code !== 'EEXIST') return cb(er)
// someone's got this one. see if it's valid.
- if (opts.stale) fs.stat(path, function (er, st) {
- if (er) {
- if (er.code === 'ENOENT') {
+ if (opts.stale) fs.stat(path, function (statEr, st) {
+ if (statEr) {
+ if (statEr.code === 'ENOENT') {
// expired already!
var opts_ = Object.create(opts, { stale: { value: false }})
exports.lock(path, opts_, cb)
return
}
- return cb(er)
+ return cb(statEr)
}
var age = Date.now() - st.ctime.getTime()
if (opts.stale) {
var st = fs.statSync(path)
- var age = Date.now() - st.ctime.getTime()
+ var ct = st.ctime.getTime()
+ if (!(ct % 1000) && (opts.stale % 1000)) {
+ // probably don't have subsecond resolution.
+ // round up the staleness indicator.
+ // Yes, this will be wrong 1/1000 times on platforms
+ // with subsecond stat precision, but that's acceptable
+ // in exchange for not mistakenly removing locks on
+ // most other systems.
+ opts.stale = 1000 * Math.ceil(opts.stale / 1000)
+ }
+ var age = Date.now() - ct
if (age > opts.stale) {
exports.unlockSync(path)
return exports.lockSync(path, opts)
{
"name": "lockfile",
- "version": "0.3.0",
+ "version": "0.3.2",
"main": "lockfile.js",
"directories": {
"test": "test"
},
"dependencies": {},
"devDependencies": {
- "tap": "~0.2.5"
+ "tap": "~0.2.5",
+ "touch": "0"
},
"scripts": {
"test": "tap test/*.js"
"description": "A very polite lock file utility, which endeavors to not litter, and to wait patiently for others.",
"readme": "# lockfile\n\nA very polite lock file utility, which endeavors to not litter, and to\nwait patiently for others.\n\n## Usage\n\n```javascript\nvar lockFile = require('lockfile')\n\n// opts is optional, and defaults to {}\nlockFile.lock('some-file.lock', opts, function (er, fd) {\n // if the er happens, then it failed to acquire a lock.\n // if there was not an error, then the fd is opened in\n // wx mode. If you want to write something to it, go ahead.\n\n // do my stuff, free of interruptions\n // then, some time later, do:\n lockFile.unlock('some-file.lock', function (er) {\n // er means that an error happened, and is probably bad.\n })\n})\n```\n\n## Methods\n\nSync methods return the value/throw the error, others don't. Standard\nnode fs stuff.\n\nAll known locks are removed when the process exits. Of course, it's\npossible for certain types of failures to cause this to fail, but a best\neffort is made to not be a litterbug.\n\n### lockFile.lock(path, [opts], cb)\n\nAcquire a file lock on the specified path. Returns the FD.\n\n### lockFile.lockSync(path, [opts])\n\nAcquire a file lock on the specified path\n\n### lockFile.unlock(path, cb)\n\nClose and unlink the lockfile.\n\n### lockFile.unlockSync(path)\n\nClose and unlink the lockfile.\n\n### lockFile.check(path, [opts], cb)\n\nCheck if the lockfile is locked and not stale.\n\nReturns boolean.\n\n### lockFile.checkSync(path, [opts], cb)\n\nCheck if the lockfile is locked and not stale.\n\nCallback is called with `cb(error, isLocked)`.\n\n## Options\n\n### opts.wait\n\nA number of milliseconds to wait for locks to expire before giving up.\nOnly used by lockFile.lock. Relies on fs.watch. If the lock is not\ncleared by the time the wait expires, then it returns with the original\nerror.\n\n### opts.stale\n\nA number of milliseconds before locks are considered to have expired.\n\n### opts.retries\n\nUsed by lock and lockSync. Retry `n` number of times before giving up.\n\n### opts.retryWait\n\nUsed by lock. Wait `n` milliseconds before retrying.\n",
"readmeFilename": "README.md",
- "_id": "lockfile@0.3.0",
- "_from": "lockfile@~0.3.0"
+ "bugs": {
+ "url": "https://github.com/isaacs/lockfile/issues"
+ },
+ "_id": "lockfile@0.3.2",
+ "dist": {
+ "shasum": "542261743479f9f907be30441f5d6b95e8b95722"
+ },
+ "_from": "lockfile@0.3.2",
+ "_resolved": "https://registry.npmjs.org/lockfile/-/lockfile-0.3.2.tgz"
}
var lockFile = require('../lockfile.js')
var path = require('path')
var fs = require('fs')
+var touch = require('touch')
test('setup', function (t) {
try { lockFile.unlockSync('basic-lock') } catch (er) {}
try { lockFile.unlockSync('watch-lock') } catch (er) {}
try { lockFile.unlockSync('retry-lock') } catch (er) {}
try { lockFile.unlockSync('contentious-lock') } catch (er) {}
+ try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
t.end()
})
if (er) throw er
var opts = { stale: 1 }
- setTimeout(next, 10)
+ setTimeout(next, 1000)
function next () {
lockFile.check('stale-lock', opts, function (er, locked) {
if (er) throw er
test('staleness sync test', function (t) {
var opts = { stale: 1 }
lockFile.lockSync('stale-lock')
- setTimeout(next, 10)
+ setTimeout(next, 1000)
function next () {
var locked
locked = lockFile.checkSync('stale-lock', opts)
t.end()
})
+test('wait and stale together', function (t) {
+ // first locker.
+ var interval
+ lockFile.lock('stale-wait-lock', function(er) {
+ // keep refreshing the lock, so we keep it forever
+ interval = setInterval(function() {
+ touch.sync('stale-wait-lock')
+ }, 10)
+
+ // try to get another lock. this must fail!
+ var opt = { stale: 1000, wait: 2000 }
+ lockFile.lock('stale-wait-lock', opt, function (er) {
+ if (!er)
+ t.fail('got second lock? that unpossible!')
+ else
+ t.pass('second lock failed, as i have foreseen it')
+ clearInterval(interval)
+ t.end()
+ })
+ })
+})
+
test('cleanup', function (t) {
try { lockFile.unlockSync('basic-lock') } catch (er) {}
try { lockFile.unlockSync('sync-lock') } catch (er) {}
try { lockFile.unlockSync('watch-lock') } catch (er) {}
try { lockFile.unlockSync('retry-lock') } catch (er) {}
try { lockFile.unlockSync('contentious-lock') } catch (er) {}
+ try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
t.end()
})
// must match entire pattern
// ending in a * or ** will make it less strict.
- re = "^" + re + "$"
+ re = "^(?:" + re + ")$"
// can match anything, as long as it's not this.
if (this.negate) re = "^(?!" + re + ").*$"
"license": "BSD",
"readme": "# sigmund\n\nQuick and dirty signatures for Objects.\n\nThis is like a much faster `deepEquals` comparison, which returns a\nstring key suitable for caches and the like.\n\n## Usage\n\n```javascript\nfunction doSomething (someObj) {\n var key = sigmund(someObj, maxDepth) // max depth defaults to 10\n var cached = cache.get(key)\n if (cached) return cached)\n\n var result = expensiveCalculation(someObj)\n cache.set(key, result)\n return result\n}\n```\n\nThe resulting key will be as unique and reproducible as calling\n`JSON.stringify` or `util.inspect` on the object, but is much faster.\nIn order to achieve this speed, some differences are glossed over.\nFor example, the object `{0:'foo'}` will be treated identically to the\narray `['foo']`.\n\nAlso, just as there is no way to summon the soul from the scribblings\nof a cocain-addled psychoanalyst, there is no way to revive the object\nfrom the signature string that sigmund gives you. In fact, it's\nbarely even readable.\n\nAs with `sys.inspect` and `JSON.stringify`, larger objects will\nproduce larger signature strings.\n\nBecause sigmund is a bit less strict than the more thorough\nalternatives, the strings will be shorter, and also there is a\nslightly higher chance for collisions. For example, these objects\nhave the same signature:\n\n var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}\n var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}\n\nLike a good Freudian, sigmund is most effective when you already have\nsome understanding of what you're looking for. It can help you help\nyourself, but you must be willing to do some work as well.\n\nCycles are handled, and cyclical objects are silently omitted (though\nthe key is included in the signature output.)\n\nThe second argument is the maximum depth, which defaults to 10,\nbecause that is the maximum object traversal depth covered by most\ninsurance carriers.\n",
"readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/isaacs/sigmund/issues"
+ },
"_id": "sigmund@1.0.0",
- "_from": "sigmund@~1.0.0"
+ "dist": {
+ "shasum": "66a2b3a749ae8b5fb89efd4fcc01dc94fbe02296"
+ },
+ "_from": "sigmund@~1.0.0",
+ "_resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
}
},
"name": "minimatch",
"description": "a glob matcher in javascript",
- "version": "0.2.11",
+ "version": "0.2.12",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/minimatch.git"
},
"readme": "# minimatch\n\nA minimal matching utility.\n\n[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch)\n\n\nThis is the matching library used internally by npm.\n\nEventually, it will replace the C binding in node-glob.\n\nIt works by converting glob expressions into JavaScript `RegExp`\nobjects.\n\n## Usage\n\n```javascript\nvar minimatch = require(\"minimatch\")\n\nminimatch(\"bar.foo\", \"*.foo\") // true!\nminimatch(\"bar.foo\", \"*.bar\") // false!\n```\n\n## Features\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n\n### Comparisons to other fnmatch/glob implementations\n\nWhile strict compliance with the existing standards is a worthwhile\ngoal, some discrepancies exist between minimatch and other\nimplementations, and are intentional.\n\nIf the pattern starts with a `!` character, then it is negated. Set the\n`nonegate` flag to suppress this behavior, and treat leading `!`\ncharacters normally. This is perhaps relevant if you wish to start the\npattern with a negative extglob pattern like `!(a|B)`. Multiple `!`\ncharacters at the start of a pattern will negate the pattern multiple\ntimes.\n\nIf a pattern starts with `#`, then it is treated as a comment, and\nwill not match anything. Use `\\#` to match a literal `#` at the\nstart of a line, or set the `nocomment` flag to suppress this behavior.\n\nThe double-star character `**` is supported by default, unless the\n`noglobstar` flag is set. This is supported in the manner of bsdglob\nand bash 4.1, where `**` only has special significance if it is the only\nthing in a path part. That is, `a/**/b` will match `a/x/y/b`, but\n`a/**b` will not. **Note that this is different from the way that `**` is\nhandled by ruby's `Dir` class.**\n\nIf an escaped pattern has no matches, and the `nonull` flag is set,\nthen minimatch.match returns the pattern as-provided, rather than\ninterpreting the character escapes. For example,\n`minimatch.match([], \"\\\\*a\\\\?\")` will return `\"\\\\*a\\\\?\"` rather than\n`\"*a?\"`. This is akin to setting the `nullglob` option in bash, except\nthat it does not resolve escaped pattern characters.\n\nIf brace expansion is not disabled, then it is performed before any\nother interpretation of the glob pattern. Thus, a pattern like\n`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded\n**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are\nchecked for validity. Since those two are valid, matching proceeds.\n\n\n## Minimatch Class\n\nCreate a minimatch object by instanting the `minimatch.Minimatch` class.\n\n```javascript\nvar Minimatch = require(\"minimatch\").Minimatch\nvar mm = new Minimatch(pattern, options)\n```\n\n### Properties\n\n* `pattern` The original pattern the minimatch object represents.\n* `options` The options supplied to the constructor.\n* `set` A 2-dimensional array of regexp or string expressions.\n Each row in the\n array corresponds to a brace-expanded pattern. Each item in the row\n corresponds to a single path-part. For example, the pattern\n `{a,b/c}/d` would expand to a set of patterns like:\n\n [ [ a, d ]\n , [ b, c, d ] ]\n\n If a portion of the pattern doesn't have any \"magic\" in it\n (that is, it's something like `\"foo\"` rather than `fo*o?`), then it\n will be left as a string rather than converted to a regular\n expression.\n\n* `regexp` Created by the `makeRe` method. A single regular expression\n expressing the entire pattern. This is useful in cases where you wish\n to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.\n* `negate` True if the pattern is negated.\n* `comment` True if the pattern is a comment.\n* `empty` True if the pattern is `\"\"`.\n\n### Methods\n\n* `makeRe` Generate the `regexp` member if necessary, and return it.\n Will return `false` if the pattern is invalid.\n* `match(fname)` Return true if the filename matches the pattern, or\n false otherwise.\n* `matchOne(fileArray, patternArray, partial)` Take a `/`-split\n filename, and match it against a single row in the `regExpSet`. This\n method is mainly for internal use, but is exposed so that it can be\n used by a glob-walker that needs to avoid excessive filesystem calls.\n\nAll other methods are internal, and will be called as necessary.\n\n## Functions\n\nThe top-level exported function has a `cache` property, which is an LRU\ncache set to store 100 items. So, calling these methods repeatedly\nwith the same pattern and options will use the same Minimatch object,\nsaving the cost of parsing it multiple times.\n\n### minimatch(path, pattern, options)\n\nMain export. Tests a path against the pattern using the options.\n\n```javascript\nvar isJS = minimatch(file, \"*.js\", { matchBase: true })\n```\n\n### minimatch.filter(pattern, options)\n\nReturns a function that tests its\nsupplied argument, suitable for use with `Array.filter`. Example:\n\n```javascript\nvar javascripts = fileList.filter(minimatch.filter(\"*.js\", {matchBase: true}))\n```\n\n### minimatch.match(list, pattern, options)\n\nMatch against the list of\nfiles, in the style of fnmatch or glob. If nothing is matched, and\noptions.nonull is set, then return a list containing the pattern itself.\n\n```javascript\nvar javascripts = minimatch.match(fileList, \"*.js\", {matchBase: true}))\n```\n\n### minimatch.makeRe(pattern, options)\n\nMake a regular expression object from the pattern.\n\n## Options\n\nAll options are `false` by default.\n\n### debug\n\nDump a ton of stuff to stderr.\n\n### nobrace\n\nDo not expand `{a,b}` and `{1..3}` brace sets.\n\n### noglobstar\n\nDisable `**` matching against multiple folder names.\n\n### dot\n\nAllow patterns to match filenames starting with a period, even if\nthe pattern does not explicitly have a period in that spot.\n\nNote that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`\nis set.\n\n### noext\n\nDisable \"extglob\" style patterns like `+(a|b)`.\n\n### nocase\n\nPerform a case-insensitive match.\n\n### nonull\n\nWhen a match is not found by `minimatch.match`, return a list containing\nthe pattern itself. When set, an empty list is returned if there are\nno matches.\n\n### matchBase\n\nIf set, then patterns without slashes will be matched\nagainst the basename of the path if it contains slashes. For example,\n`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.\n\n### nocomment\n\nSuppress the behavior of treating `#` at the start of a pattern as a\ncomment.\n\n### nonegate\n\nSuppress the behavior of treating a leading `!` character as negation.\n\n### flipNegate\n\nReturns from negate expressions the same as if they were not negated.\n(Ie, true on a hit, false on a miss.)\n",
"readmeFilename": "README.md",
- "_id": "minimatch@0.2.11",
+ "bugs": {
+ "url": "https://github.com/isaacs/minimatch/issues"
+ },
+ "_id": "minimatch@0.2.12",
"_from": "minimatch@latest"
}
, next = files.concat([ "a-b", "aXb"
, ".x", ".y" ])
+
+var patterns =
+ [ "http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test"
+ , ["a*", ["a", "abc", "abd", "abe"]]
+ , ["X*", ["X*"], {nonull: true}]
+
+ // allow null glob expansion
+ , ["X*", []]
+
+ // isaacs: Slightly different than bash/sh/ksh
+ // \\* is not un-escaped to literal "*" in a failed match,
+ // but it does make it get treated as a literal star
+ , ["\\*", ["\\*"], {nonull: true}]
+ , ["\\**", ["\\**"], {nonull: true}]
+ , ["\\*\\*", ["\\*\\*"], {nonull: true}]
+
+ , ["b*/", ["bdir/"]]
+ , ["c*", ["c", "ca", "cb"]]
+ , ["**", files]
+
+ , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
+ , ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
+
+ , "legendary larry crashes bashes"
+ , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
+ , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
+ , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
+ , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
+
+ , "character classes"
+ , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
+ , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
+ "bdir/", "ca", "cb", "dd", "de"]]
+ , ["a*[^c]", ["abd", "abe"]]
+ , function () { files.push("a-b", "aXb") }
+ , ["a[X-]b", ["a-b", "aXb"]]
+ , function () { files.push(".x", ".y") }
+ , ["[^a-c]*", ["d", "dd", "de"]]
+ , function () { files.push("a*b/", "a*b/ooo") }
+ , ["a\\*b/*", ["a*b/ooo"]]
+ , ["a\\*?/*", ["a*b/ooo"]]
+ , ["*\\\\!*", [], {null: true}, ["echo !7"]]
+ , ["*\\!*", ["echo !7"], null, ["echo !7"]]
+ , ["*.\\*", ["r.*"], null, ["r.*"]]
+ , ["a[b]c", ["abc"]]
+ , ["a[\\b]c", ["abc"]]
+ , ["a?c", ["abc"]]
+ , ["a\\*c", [], {null: true}, ["abc"]]
+ , ["", [""], { null: true }, [""]]
+
+ , "http://www.opensource.apple.com/source/bash/bash-23/" +
+ "bash/tests/glob-test"
+ , function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
+ , ["*/man*/bash.*", ["man/man1/bash.1"]]
+ , ["man/man1/bash.1", ["man/man1/bash.1"]]
+ , ["a***c", ["abc"], null, ["abc"]]
+ , ["a*****?c", ["abc"], null, ["abc"]]
+ , ["?*****??", ["abc"], null, ["abc"]]
+ , ["*****??", ["abc"], null, ["abc"]]
+ , ["?*****?c", ["abc"], null, ["abc"]]
+ , ["?***?****c", ["abc"], null, ["abc"]]
+ , ["?***?****?", ["abc"], null, ["abc"]]
+ , ["?***?****", ["abc"], null, ["abc"]]
+ , ["*******c", ["abc"], null, ["abc"]]
+ , ["*******?", ["abc"], null, ["abc"]]
+ , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
+ , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
+ , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
+ , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
+ , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
+ , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
+ , ["[-abc]", ["-"], null, ["-"]]
+ , ["[abc-]", ["-"], null, ["-"]]
+ , ["\\", ["\\"], null, ["\\"]]
+ , ["[\\\\]", ["\\"], null, ["\\"]]
+ , ["[[]", ["["], null, ["["]]
+ , ["[", ["["], null, ["["]]
+ , ["[*", ["[abc"], null, ["[abc"]]
+ , "a right bracket shall lose its special meaning and\n" +
+ "represent itself in a bracket expression if it occurs\n" +
+ "first in the list. -- POSIX.2 2.8.3.2"
+ , ["[]]", ["]"], null, ["]"]]
+ , ["[]-]", ["]"], null, ["]"]]
+ , ["[a-\z]", ["p"], null, ["p"]]
+ , ["??**********?****?", [], { null: true }, ["abc"]]
+ , ["??**********?****c", [], { null: true }, ["abc"]]
+ , ["?************c****?****", [], { null: true }, ["abc"]]
+ , ["*c*?**", [], { null: true }, ["abc"]]
+ , ["a*****c*?**", [], { null: true }, ["abc"]]
+ , ["a********???*******", [], { null: true }, ["abc"]]
+ , ["[]", [], { null: true }, ["a"]]
+ , ["[abc", [], { null: true }, ["["]]
+
+ , "nocase tests"
+ , ["XYZ", ["xYz"], { nocase: true, null: true }
+ , ["xYz", "ABC", "IjK"]]
+ , ["ab*", ["ABC"], { nocase: true, null: true }
+ , ["xYz", "ABC", "IjK"]]
+ , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
+ , ["xYz", "ABC", "IjK"]]
+
+ // [ pattern, [matches], MM opts, files, TAP opts]
+ , "onestar/twostar"
+ , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
+ , ["{/?,*}", ["/a", "bb"], {null: true}
+ , ["/a", "/b/b", "/a/b/c", "bb"]]
+
+ , "dots should not match unless requested"
+ , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
+
+ // .. and . can only match patterns starting with .,
+ // even when options.dot is set.
+ , function () {
+ files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
+ }
+ , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
+ , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
+ , ["a/*/b", ["a/c/b"], {dot:false}]
+ , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
+
+
+ // this also tests that changing the options needs
+ // to change the cache key, even if the pattern is
+ // the same!
+ , ["**", ["a/b","a/.d",".a/.d"], { dot: true }
+ , [ ".a/.d", "a/.d", "a/b"]]
+
+ , "paren sets cannot contain slashes"
+ , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
+
+ // brace sets trump all else.
+ //
+ // invalid glob pattern. fails on bash4 and bsdglob.
+ // however, in this implementation, it's easier just
+ // to do the intuitive thing, and let brace-expansion
+ // actually come before parsing any extglob patterns,
+ // like the documentation seems to say.
+ //
+ // XXX: if anyone complains about this, either fix it
+ // or tell them to grow up and stop complaining.
+ //
+ // bash/bsdglob says this:
+ // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
+ // but we do this instead:
+ , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
+
+ // test partial parsing in the presence of comment/negation chars
+ , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
+ , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
+
+ // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
+ , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
+ , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
+ , {}
+ , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
+
+
+ // crazy nested {,,} and *(||) tests.
+ , function () {
+ files = [ "a", "b", "c", "d"
+ , "ab", "ac", "ad"
+ , "bc", "cb"
+ , "bc,d", "c,db", "c,d"
+ , "d)", "(b|c", "*(b|c"
+ , "b|c", "b|cc", "cb|c"
+ , "x(a|b|c)", "x(a|c)"
+ , "(a|b|c)", "(a|c)"]
+ }
+ , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
+ , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
+ // a
+ // *(b|c)
+ // *(b|d)
+ , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
+ , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
+
+
+ // test various flag settings.
+ , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
+ , { noext: true } ]
+ , ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
+ , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
+ , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
+
+
+ // begin channelling Boole and deMorgan...
+ , "negation tests"
+ , function () {
+ files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
+ }
+
+ // anything that is NOT a* matches.
+ , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
+
+ // anything that IS !a* matches.
+ , ["!a*", ["!ab", "!abc"], {nonegate: true}]
+
+ // anything that IS a* matches
+ , ["!!a*", ["a!b"]]
+
+ // anything that is NOT !a* matches
+ , ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
+
+ // negation nestled within a pattern
+ , function () {
+ files = [ "foo.js"
+ , "foo.bar"
+ // can't match this one without negative lookbehind.
+ , "foo.js.js"
+ , "blar.js"
+ , "foo."
+ , "boo.js.boo" ]
+ }
+ , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
+
+ // https://github.com/isaacs/minimatch/issues/5
+ , function () {
+ files = [ 'a/b/.x/c'
+ , 'a/b/.x/c/d'
+ , 'a/b/.x/c/d/e'
+ , 'a/b/.x'
+ , 'a/b/.x/'
+ , 'a/.x/b'
+ , '.x'
+ , '.x/'
+ , '.x/a'
+ , '.x/a/b'
+ , 'a/.x/b/.x/c'
+ , '.x/.x' ]
+ }
+ , ["**/.x/**", [ '.x/'
+ , '.x/a'
+ , '.x/a/b'
+ , 'a/.x/b'
+ , 'a/b/.x/'
+ , 'a/b/.x/c'
+ , 'a/b/.x/c/d'
+ , 'a/b/.x/c/d/e' ] ]
+
+ ]
+
+var regexps =
+ [ '/^(?:(?=.)a[^/]*?)$/',
+ '/^(?:(?=.)X[^/]*?)$/',
+ '/^(?:(?=.)X[^/]*?)$/',
+ '/^(?:\\*)$/',
+ '/^(?:(?=.)\\*[^/]*?)$/',
+ '/^(?:\\*\\*)$/',
+ '/^(?:(?=.)b[^/]*?\\/)$/',
+ '/^(?:(?=.)c[^/]*?)$/',
+ '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
+ '/^(?:\\.\\.\\/(?!\\.)(?=.)[^/]*?\\/)$/',
+ '/^(?:s\\/(?=.)\\.\\.[^/]*?\\/)$/',
+ '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/1\\/)$/',
+ '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/\u0001\\/)$/',
+ '/^(?:(?!\\.)(?=.)[a-c]b[^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[a-y][^/]*?[^c])$/',
+ '/^(?:(?=.)a[^/]*?[^c])$/',
+ '/^(?:(?=.)a[X-]b)$/',
+ '/^(?:(?!\\.)(?=.)[^a-c][^/]*?)$/',
+ '/^(?:a\\*b\\/(?!\\.)(?=.)[^/]*?)$/',
+ '/^(?:(?=.)a\\*[^/]\\/(?!\\.)(?=.)[^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\\\\\![^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\![^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\.\\*)$/',
+ '/^(?:(?=.)a[b]c)$/',
+ '/^(?:(?=.)a[b]c)$/',
+ '/^(?:(?=.)a[^/]c)$/',
+ '/^(?:a\\*c)$/',
+ 'false',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\/(?=.)man[^/]*?\\/(?=.)bash\\.[^/]*?)$/',
+ '/^(?:man\\/man1\\/bash\\.1)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?c)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
+ '/^(?:(?=.)a[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k[^/]*?[^/]*?[^/]*?)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k[^/]*?[^/]*?)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[-abc])$/',
+ '/^(?:(?!\\.)(?=.)[abc-])$/',
+ '/^(?:\\\\)$/',
+ '/^(?:(?!\\.)(?=.)[\\\\])$/',
+ '/^(?:(?!\\.)(?=.)[\\[])$/',
+ '/^(?:\\[)$/',
+ '/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[\\]])$/',
+ '/^(?:(?!\\.)(?=.)[\\]-])$/',
+ '/^(?:(?!\\.)(?=.)[a-z])$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/',
+ '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/',
+ '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/',
+ '/^(?:\\[\\])$/',
+ '/^(?:\\[abc)$/',
+ '/^(?:(?=.)XYZ)$/i',
+ '/^(?:(?=.)ab[^/]*?)$/i',
+ '/^(?:(?!\\.)(?=.)[ia][^/][ck])$/i',
+ '/^(?:\\/(?!\\.)(?=.)[^/]*?|(?!\\.)(?=.)[^/]*?)$/',
+ '/^(?:\\/(?!\\.)(?=.)[^/]|(?!\\.)(?=.)[^/]*?)$/',
+ '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/',
+ '/^(?:a\\/(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)[^/]*?\\/b)$/',
+ '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
+ '/^(?:a\\/(?!\\.)(?=.)[^/]*?\\/b)$/',
+ '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/',
+ '/^(?:(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\/b\\))$/',
+ '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
+ '/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/',
+ '/^(?:(?=.)\\[(?=.)#a[^/]*?)$/',
+ '/^(?:(?=.)\\+\\(a\\|[^/]*?\\|c\\\\\\\\\\|d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|f\\\\\\\\\\\\\\\\\\|g)$/',
+ '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/',
+ '/^(?:a|(?!\\.)(?=.)[^/]*?\\(b\\|c|d\\))$/',
+ '/^(?:a|(?!\\.)(?=.)(?:b|c)*|(?!\\.)(?=.)(?:b|d)*)$/',
+ '/^(?:(?!\\.)(?=.)(?:a|b|c)*|(?!\\.)(?=.)(?:a|c)*)$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\|b\\|c\\)|(?!\\.)(?=.)[^/]*?\\(a\\|c\\))$/',
+ '/^(?:(?=.)a[^/]b)$/',
+ '/^(?:(?=.)#[^/]*?)$/',
+ '/^(?!^(?:(?=.)a[^/]*?)$).*$/',
+ '/^(?:(?=.)\\!a[^/]*?)$/',
+ '/^(?:(?=.)a[^/]*?)$/',
+ '/^(?!^(?:(?=.)\\!a[^/]*?)$).*$/',
+ '/^(?:(?!\\.)(?=.)[^/]*?\\.(?:(?!js)[^/]*?))$/',
+ '/^(?:(?:(?!(?:\\/|^)\\.).)*?\\/\\.x\\/(?:(?!(?:\\/|^)\\.).)*?)$/' ]
+var re = 0;
+
tap.test("basic tests", function (t) {
var start = Date.now()
// [ pattern, [matches], MM opts, files, TAP opts]
- ; [ "http://www.bashcookbook.com/bashinfo" +
- "/source/bash-1.14.7/tests/glob-test"
- , ["a*", ["a", "abc", "abd", "abe"]]
- , ["X*", ["X*"], {nonull: true}]
-
- // allow null glob expansion
- , ["X*", []]
-
- // isaacs: Slightly different than bash/sh/ksh
- // \\* is not un-escaped to literal "*" in a failed match,
- // but it does make it get treated as a literal star
- , ["\\*", ["\\*"], {nonull: true}]
- , ["\\**", ["\\**"], {nonull: true}]
- , ["\\*\\*", ["\\*\\*"], {nonull: true}]
-
- , ["b*/", ["bdir/"]]
- , ["c*", ["c", "ca", "cb"]]
- , ["**", files]
-
- , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}]
- , ["s/\\..*//", ["s/\\..*//"], {nonull: true}]
-
- , "legendary larry crashes bashes"
- , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"
- , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}]
- , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"
- , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}]
-
- , "character classes"
- , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]]
- , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd",
- "bdir/", "ca", "cb", "dd", "de"]]
- , ["a*[^c]", ["abd", "abe"]]
- , function () { files.push("a-b", "aXb") }
- , ["a[X-]b", ["a-b", "aXb"]]
- , function () { files.push(".x", ".y") }
- , ["[^a-c]*", ["d", "dd", "de"]]
- , function () { files.push("a*b/", "a*b/ooo") }
- , ["a\\*b/*", ["a*b/ooo"]]
- , ["a\\*?/*", ["a*b/ooo"]]
- , ["*\\\\!*", [], {null: true}, ["echo !7"]]
- , ["*\\!*", ["echo !7"], null, ["echo !7"]]
- , ["*.\\*", ["r.*"], null, ["r.*"]]
- , ["a[b]c", ["abc"]]
- , ["a[\\b]c", ["abc"]]
- , ["a?c", ["abc"]]
- , ["a\\*c", [], {null: true}, ["abc"]]
- , ["", [""], { null: true }, [""]]
-
- , "http://www.opensource.apple.com/source/bash/bash-23/" +
- "bash/tests/glob-test"
- , function () { files.push("man/", "man/man1/", "man/man1/bash.1") }
- , ["*/man*/bash.*", ["man/man1/bash.1"]]
- , ["man/man1/bash.1", ["man/man1/bash.1"]]
- , ["a***c", ["abc"], null, ["abc"]]
- , ["a*****?c", ["abc"], null, ["abc"]]
- , ["?*****??", ["abc"], null, ["abc"]]
- , ["*****??", ["abc"], null, ["abc"]]
- , ["?*****?c", ["abc"], null, ["abc"]]
- , ["?***?****c", ["abc"], null, ["abc"]]
- , ["?***?****?", ["abc"], null, ["abc"]]
- , ["?***?****", ["abc"], null, ["abc"]]
- , ["*******c", ["abc"], null, ["abc"]]
- , ["*******?", ["abc"], null, ["abc"]]
- , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
- , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
- , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]]
- , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]]
- , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]]
- , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]]
- , ["[-abc]", ["-"], null, ["-"]]
- , ["[abc-]", ["-"], null, ["-"]]
- , ["\\", ["\\"], null, ["\\"]]
- , ["[\\\\]", ["\\"], null, ["\\"]]
- , ["[[]", ["["], null, ["["]]
- , ["[", ["["], null, ["["]]
- , ["[*", ["[abc"], null, ["[abc"]]
- , "a right bracket shall lose its special meaning and\n" +
- "represent itself in a bracket expression if it occurs\n" +
- "first in the list. -- POSIX.2 2.8.3.2"
- , ["[]]", ["]"], null, ["]"]]
- , ["[]-]", ["]"], null, ["]"]]
- , ["[a-\z]", ["p"], null, ["p"]]
- , ["??**********?****?", [], { null: true }, ["abc"]]
- , ["??**********?****c", [], { null: true }, ["abc"]]
- , ["?************c****?****", [], { null: true }, ["abc"]]
- , ["*c*?**", [], { null: true }, ["abc"]]
- , ["a*****c*?**", [], { null: true }, ["abc"]]
- , ["a********???*******", [], { null: true }, ["abc"]]
- , ["[]", [], { null: true }, ["a"]]
- , ["[abc", [], { null: true }, ["["]]
-
- , "nocase tests"
- , ["XYZ", ["xYz"], { nocase: true, null: true }
- , ["xYz", "ABC", "IjK"]]
- , ["ab*", ["ABC"], { nocase: true, null: true }
- , ["xYz", "ABC", "IjK"]]
- , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true }
- , ["xYz", "ABC", "IjK"]]
-
- // [ pattern, [matches], MM opts, files, TAP opts]
- , "onestar/twostar"
- , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]]
- , ["{/?,*}", ["/a", "bb"], {null: true}
- , ["/a", "/b/b", "/a/b/c", "bb"]]
-
- , "dots should not match unless requested"
- , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]]
-
- // .. and . can only match patterns starting with .,
- // even when options.dot is set.
- , function () {
- files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"]
- }
- , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}]
- , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}]
- , ["a/*/b", ["a/c/b"], {dot:false}]
- , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}]
-
-
- // this also tests that changing the options needs
- // to change the cache key, even if the pattern is
- // the same!
- , ["**", ["a/b","a/.d",".a/.d"], { dot: true }
- , [ ".a/.d", "a/.d", "a/b"]]
-
- , "paren sets cannot contain slashes"
- , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]]
-
- // brace sets trump all else.
- //
- // invalid glob pattern. fails on bash4 and bsdglob.
- // however, in this implementation, it's easier just
- // to do the intuitive thing, and let brace-expansion
- // actually come before parsing any extglob patterns,
- // like the documentation seems to say.
- //
- // XXX: if anyone complains about this, either fix it
- // or tell them to grow up and stop complaining.
- //
- // bash/bsdglob says this:
- // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]]
- // but we do this instead:
- , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]]
-
- // test partial parsing in the presence of comment/negation chars
- , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]]
- , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]]
-
- // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped.
- , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g"
- , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"]
- , {}
- , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]]
-
-
- // crazy nested {,,} and *(||) tests.
- , function () {
- files = [ "a", "b", "c", "d"
- , "ab", "ac", "ad"
- , "bc", "cb"
- , "bc,d", "c,db", "c,d"
- , "d)", "(b|c", "*(b|c"
- , "b|c", "b|cc", "cb|c"
- , "x(a|b|c)", "x(a|c)"
- , "(a|b|c)", "(a|c)"]
- }
- , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]]
- , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]]
- // a
- // *(b|c)
- // *(b|d)
- , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]]
- , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]]
-
-
- // test various flag settings.
- , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"]
- , { noext: true } ]
- , ["a?b", ["x/y/acb", "acb/"], {matchBase: true}
- , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ]
- , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]]
-
-
- // begin channelling Boole and deMorgan...
- , "negation tests"
- , function () {
- files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"]
- }
-
- // anything that is NOT a* matches.
- , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]]
-
- // anything that IS !a* matches.
- , ["!a*", ["!ab", "!abc"], {nonegate: true}]
-
- // anything that IS a* matches
- , ["!!a*", ["a!b"]]
-
- // anything that is NOT !a* matches
- , ["!\\!a*", ["a!b", "d", "e", "\\!a"]]
-
- // negation nestled within a pattern
- , function () {
- files = [ "foo.js"
- , "foo.bar"
- // can't match this one without negative lookbehind.
- , "foo.js.js"
- , "blar.js"
- , "foo."
- , "boo.js.boo" ]
- }
- , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ]
-
- // https://github.com/isaacs/minimatch/issues/5
- , function () {
- files = [ 'a/b/.x/c'
- , 'a/b/.x/c/d'
- , 'a/b/.x/c/d/e'
- , 'a/b/.x'
- , 'a/b/.x/'
- , 'a/.x/b'
- , '.x'
- , '.x/'
- , '.x/a'
- , '.x/a/b'
- , 'a/.x/b/.x/c'
- , '.x/.x' ]
- }
- , ["**/.x/**", [ '.x/'
- , '.x/a'
- , '.x/a/b'
- , 'a/.x/b'
- , 'a/b/.x/'
- , 'a/b/.x/c'
- , 'a/b/.x/c/d'
- , 'a/b/.x/c/d/e' ] ]
-
- ].forEach(function (c) {
- if (typeof c === "function") return c()
- if (typeof c === "string") return t.comment(c)
-
- var pattern = c[0]
- , expect = c[1].sort(alpha)
- , options = c[2] || {}
- , f = c[3] || files
- , tapOpts = c[4] || {}
-
- // options.debug = true
- var m = new mm.Minimatch(pattern, options)
- var r = m.makeRe()
- tapOpts.re = String(r) || JSON.stringify(r)
- tapOpts.files = JSON.stringify(f)
- tapOpts.pattern = pattern
- tapOpts.set = m.set
- tapOpts.negated = m.negate
-
- var actual = mm.match(f, pattern, options)
- actual.sort(alpha)
-
- t.equivalent( actual, expect
- , JSON.stringify(pattern) + " " + JSON.stringify(expect)
- , tapOpts )
- })
+ patterns.forEach(function (c) {
+ if (typeof c === "function") return c()
+ if (typeof c === "string") return t.comment(c)
+
+ var pattern = c[0]
+ , expect = c[1].sort(alpha)
+ , options = c[2] || {}
+ , f = c[3] || files
+ , tapOpts = c[4] || {}
+
+ // options.debug = true
+ var m = new mm.Minimatch(pattern, options)
+ var r = m.makeRe()
+ var expectRe = regexps[re++]
+ tapOpts.re = String(r) || JSON.stringify(r)
+ tapOpts.files = JSON.stringify(f)
+ tapOpts.pattern = pattern
+ tapOpts.set = m.set
+ tapOpts.negated = m.negate
+
+ var actual = mm.match(f, pattern, options)
+ actual.sort(alpha)
+
+ t.equivalent( actual, expect
+ , JSON.stringify(pattern) + " " + JSON.stringify(expect)
+ , tapOpts )
+
+ t.equal(tapOpts.re, expectRe, tapOpts)
+ })
t.comment("time=" + (Date.now() - start) + "ms")
t.end()
},
"readme": "node-gyp\n=========\n### Node.js native addon build tool\n\n`node-gyp` is a cross-platform command-line tool written in Node.js for compiling\nnative addon modules for Node.js, which takes away the pain of dealing with the\nvarious differences in build platforms. It is the replacement to the `node-waf`\nprogram which is removed for node `v0.8`. If you have a native addon for node that\nstill has a `wscript` file, then you should definitely add a `binding.gyp` file\nto support the latest versions of node.\n\nMultiple target versions of node are supported (i.e. `0.6`, `0.7`,..., `1.0`,\netc.), regardless of what version of node is actually installed on your system\n(`node-gyp` downloads the necessary development files for the target version).\n\n#### Features:\n\n * Easy to use, consistent interface\n * Same commands to build your module on every platform\n * Supports multiple target versions of Node\n\n\nInstallation\n------------\n\nYou can install with `npm`:\n\n``` bash\n$ npm install -g node-gyp\n```\n\nYou will also need to install:\n\n * On Unix:\n * `python`\n * `make`\n * A proper C/C++ compiler toolchain, like GCC\n * On Windows:\n * [Python][windows-python] ([`v2.7.3`][windows-python-v2.7.3] recommended, `v3.x.x` is __*not*__ supported)\n * Windows XP/Vista/7:\n * Microsoft Visual Studio C++ 2010 ([Express][msvc2010] version works well)\n * For 64-bit builds of node and native modules you will _**also**_ need the [Windows 7 64-bit SDK][win7sdk]\n * If the install fails, try uninstalling any C++ 2010 x64&x86 Redistributable that you have installed first.\n * If you get errors that the 64-bit compilers are not installed you may also need the [compiler update for the Windows SDK 7.1]\n * Windows 8:\n * Microsoft Visual Studio C++ 2012 for Windows Desktop ([Express][msvc2012] version works well)\n\nNote that OS X is just a flavour of Unix and so needs `python`, `make`, and C/C++.\nAn easy way to obtain these is to install XCode from Apple,\nand then use it to install the command line tools (under Preferences -> Downloads).\n\nHow to Use\n----------\n\nTo compile your native addon, first go to its root directory:\n\n``` bash\n$ cd my_node_addon\n```\n\nThe next step is to generate the appropriate project build files for the current\nplatform. Use `configure` for that:\n\n``` bash\n$ node-gyp configure\n```\n\n__Note__: The `configure` step looks for the `binding.gyp` file in the current\ndirectory to processs. See below for instructions on creating the `binding.gyp` file.\n\nNow you will have either a `Makefile` (on Unix platforms) or a `vcxproj` file\n(on Windows) in the `build/` directory. Next invoke the `build` command:\n\n``` bash\n$ node-gyp build\n```\n\nNow you have your compiled `.node` bindings file! The compiled bindings end up\nin `build/Debug/` or `build/Release/`, depending on the build mode. At this point\nyou can require the `.node` file with Node and run your tests!\n\n__Note:__ To create a _Debug_ build of the bindings file, pass the `--debug` (or\n`-d`) switch when running the either `configure` or `build` command.\n\n\nThe \"binding.gyp\" file\n----------------------\n\nPreviously when node had `node-waf` you had to write a `wscript` file. The\nreplacement for that is the `binding.gyp` file, which describes the configuration\nto build your module in a JSON-like format. This file gets placed in the root of\nyour package, alongside the `package.json` file.\n\nA barebones `gyp` file appropriate for building a node addon looks like:\n\n``` json\n{\n \"targets\": [\n {\n \"target_name\": \"binding\",\n \"sources\": [ \"src/binding.cc\" ]\n }\n ]\n}\n```\n\nSome additional resources for writing `gyp` files:\n\n * [\"Hello World\" node addon example](https://github.com/joyent/node/tree/master/test/addons/hello-world)\n * [gyp user documentation](http://code.google.com/p/gyp/wiki/GypUserDocumentation)\n * [gyp input format reference](http://code.google.com/p/gyp/wiki/InputFormatReference)\n * [*\"binding.gyp\" files out in the wild* wiki page](https://github.com/TooTallNate/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild)\n\n\nCommands\n--------\n\n`node-gyp` responds to the following commands:\n\n| **Command** | **Description**\n|:--------------|:---------------------------------------------------------------\n| `build` | Invokes `make`/`msbuild.exe` and builds the native addon\n| `clean` | Removes any the `build` dir if it exists\n| `configure` | Generates project build files for the current platform\n| `rebuild` | Runs \"clean\", \"configure\" and \"build\" all in a row\n| `install` | Installs node development header files for the given version\n| `list` | Lists the currently installed node development file versions\n| `remove` | Removes the node development header files for the given version\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n[windows-python]: http://www.python.org/getit/windows\n[windows-python-v2.7.3]: http://www.python.org/download/releases/2.7.3#download\n[msvc2010]: http://go.microsoft.com/?linkid=9709949\n[msvc2012]: http://go.microsoft.com/?linkid=9816758\n[win7sdk]: http://www.microsoft.com/en-us/download/details.aspx?id=8279\n[compiler update for the Windows SDK 7.1]: http://www.microsoft.com/en-us/download/details.aspx?id=4422\n",
"readmeFilename": "README.md",
- "_id": "node-gyp@0.9.5",
- "dist": {
- "shasum": "43482999c1233e1d16e949a58f28b493d56b5b1d"
+ "bugs": {
+ "url": "https://github.com/TooTallNate/node-gyp/issues"
},
- "_from": "node-gyp@0.9.5",
- "_resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-0.9.5.tgz"
+ "_id": "node-gyp@0.9.5",
+ "_from": "node-gyp@latest"
}
fs.writeFile(cache, "{}", function (er) {
if (er) return cb(new Error("Broken cache."))
return requestAll_.call(this, 0, {}, cb)
- })
+ }.bind(this))
}
var t = +data._updated || 0
requestAll_.call(this, t, data, cb)
},
"readme": "# couch-login\n\nThis module lets you log into couchdb to get a session token, then make\nrequests using that session. It is basically just a thin wrapper around\n[@mikeal's request module](https://github.com/mikeal/request).\n\nThis is handy if you want a user to take actions in a couchdb database\non behalf of a user, without having to store their couchdb username and\npassword anywhere. (You do need to store the AuthSession token\nsomewhere, though.)\n\n## Usage\n\n```javascript\nvar CouchLogin = require('couch-login')\n\n// Nothing about this module is http-server specific of course.\n// You could also use it to do authenticated requests against\n// a couchdb using sessions and storing the token somewhere else.\n\nhttp.createServer(function (req, res) {\n var couch = new CouchLogin('http://my-couch.iriscouch.com:5984/')\n\n // .. look up the token in the user's session or whatever ..\n // Look at couch.decorate(req, res) for more on doing that\n // automatically, below.\n\n if (sessionToken) {\n // this user already logged in.\n couch.token = sessionToken\n\n // now we can do things on their behalf, like:\n // 1. View their session info.\n // like doing request.get({ uri: couch + '/_session', ... })\n // but with the cookie and whatnot\n\n couch.get('/_session', function (er, resp, data) {\n // er = some kind of communication error.\n // resp = response object from the couchdb request.\n // data = parsed JSON response body.\n if (er || resp.statusCode !== 200) {\n res.statusCode = resp.statusCode || 403\n return res.end('Invalid login or something')\n }\n\n // now we have the session info, we know who this user is.\n // hitting couchdb for this on every request is kinda costly,\n // so maybe you should store the username wherever you're storing\n // the sessionToken. RedSess is a good util for this, if you're\n // into redis. And if you're not into redis, you're crazy,\n // because it is awesome.\n\n // now let's get the user record.\n // note that this will 404 for anyone other than the user,\n // unless they're a server admin.\n couch.get('/_users/org.couchdb.user:' + data.userCtx.name, etc)\n\n // PUTs and DELETEs will also use their session, of course, so\n // your validate_doc_update's will see their info in userCtx\n })\n\n } else {\n // don't have a sessionToken.\n // get a username and password from the post body or something.\n // maybe redirect to a /login page or something to ask for that.\n var login = { name: name, password: password }\n couch.login(login, function (er, resp, data) {\n // again, er is an error, resp is the response obj, data is the json\n if (er || resp.statusCode !== 200) {\n res.statusCode = resp.statusCode || 403\n return res.end('Invalid login or something')\n }\n\n // the data is something like\n // {\"ok\":true,\"name\":\"testuser\",\"roles\":[]}\n // and couch.token is the token you'll need to save somewhere.\n\n // at this point, you can start making authenticated requests to\n // couchdb, or save data in their session, or do whatever it is\n // that you need to do.\n\n res.statusCode = 200\n res.write(\"Who's got two thumbs and just logged you into couch?\\n\")\n setTimeout(function () {\n res.end(\"THIS GUY!\")\n }, 500)\n })\n }\n})\n```\n\n## Class: CouchLogin\n### new CouchLogin(couchdbUrl, token)\n\nCreate a new CouchLogin object bound to the couchdb url.\n\nIn addition to these, the `get`, `post`, `put`, and `del` methods all\nproxy to the associated method on [request](https://github.com/mikeal/request).\n\nHowever, as you'll note in the example above, only the pathname portion\nof the url is required. Urls will be appended to the couchdb url passed\ninto the constructor.\n\nIf you have to talk to more than one couchdb, then you'll need more than\none CouchLogin object, for somewhat obvious reasons.\n\nAll callbacks get called with the following arguments, which are exactly\nidentical to the arguments passed to a `request` callback.\n\n* `er` {Error | null} Set if a communication error happens.\n* `resp` {HTTP Response} The response from the request to couchdb\n* `data` {Object} The parsed JSON data from couch\n\nIf the token is the string \"anonymous\", then it will not attempt to log\nin before making requests. If the token is not \"anonymous\", then it\nmust be an object with the appropriate fields.\n\n### couch.token\n\n* {Object}\n\nAn object representing the couchdb session token. (Basically just a\ncookie and a timeout.)\n\nIf the token has already timed out, then setting it will have no effect.\n\n### couch.tokenSet\n\nIf set, this method is called whenever the token is saved.\n\nFor example, you could assign a function to this method to save the\ntoken into a redis session, a cookie, or in some other database.\n\nTakes a callback which should be called when the token is saved.\n\n### couch.tokenGet\n\nIf set, this method is called to look up the token on demand.\n\nThe inverse of couch.tokenSet. Takes a callback which is called with\nthe `cb(er || null, token)`.\n\n### couch.tokenDel\n\nIf set, this method is called to delete the token when it should be\ndiscarded.\n\nRelated to tokenGet and tokenSet. Takes a callback which should be\ncalled when the token is deleted.\n\n### couch.ca\n\n* {String | Array | null}\n\nA certificate authority string, or an array of CA strings. Only\nrelevant for HTTPS couches, of course.\n\nLeave as `null` to use the default ca settings built into node.\n\n### couch.strictSSL\n\n* {Boolean | null}\n\nWhether or not to be strict about SSL connections. If left as null,\nthen use the default setting in node, which is true in node versions\n0.9.x and above, and false prior to 0.8.x.\n\nOnly relevant for HTTPS couches, of course.\n\n### couch.anonymous()\n\nReturn a new CouchLogin object that points at the same couchdb server,\nbut doesn't try to log in before making requests.\n\nThis is handy for situations where the user is not logged in at the\nmoment, but a request needs to be made anyway, and does not require\nauthorization.\n\n### couch.login(auth, callback)\n\n* `auth` {Object} The login details\n * `name` {String}\n * `password` {String}\n* `callback` {Function}\n\nWhen the callback is called, the `couch.token` will already have been\nset (assuming it worked!), so subsequent requests will be done as that\nuser.\n\n### couch.get(path, callback)\n\nGET the supplied path from the couchdb using the credentials on the\ntoken.\n\nFails if the token is invalid or expired.\n\n### couch.del(path, callback)\n\nDELETE the supplied path from the couchdb using the credentials on the\ntoken.\n\nFails if the token is invalid or expired.\n\n### couch.post(path, data, callback)\n\nPOST the data to the supplied path in the couchdb, using the credentials\non the token.\n\nFails if the token is invalid or expired.\n\n### couch.put(path, data, callback)\n\nPUT the data to the supplied path in the couchdb, using the credentials\non the token.\n\nFails if the token is invalid or expired.\n\n### couch.changePass(newAuth, callback)\n\nMust already be logged in. Updates the `_users` document with new salt\nand hash, and re-logs in with the new credentials. Callback is called\nwith the same arguments as login, or the first step of the process that\nfailed.\n\n### couch.signup(userData, callback)\n\nCreate a new user account. The userData must contain at least a `name`\nand `password` field. Any additional data will be copied to the user\nrecord. The `_id`, `name`, `roles`, `type`, `password_sha`, `salt`, and\n`date` fields are generated.\n\nAlso signs in as the newly created user, on successful account creation.\n\n### couch.deleteAccount(name, callback)\n\nDeletes a user account. If not logged in as the user, or a server\nadmin, then the request will fail.\n\nNote that this immediately invalidates any session tokens for the\ndeleted user account. If you are deleting the user's record, then you\nought to follow this with `couch.logout(callback)` so that it won't try\nto re-use the invalid session.\n\n### couch.logout(callback)\n\nDelete the session out of couchdb. This makes the token permanently\ninvalid, and deletes it.\n\n### couch.decorate(req, res)\n\nSet up `req.couch` and `res.couch` as references to this couch login\ninstance.\n\nAdditionall, if `req.session` or `res.session` is set, then it'll call\n`session.get('couch_token', cb)` as the tokenGet method,\n`session.set('couch_token', token, cb)` as the tokenSet method, and\n`session.del('couch_token', cb)` as the tokenDel method.\n\nThis works really nice with\n[RedSess](https://github.com/isaacs/redsess).\n",
"readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/isaacs/couch-login/issues"
+ },
"_id": "couch-login@0.1.16",
"_from": "couch-login@~0.1.15"
}
},
"name": "npm-registry-client",
"description": "Client for the npm registry",
- "version": "0.2.20",
+ "version": "0.2.23",
"repository": {
"url": "git://github.com/isaacs/npm-registry-client"
},
"test": "tap test/*.js"
},
"dependencies": {
- "request": "~2.16",
+ "request": "2 >=2.20.0",
"graceful-fs": "~1.2.0",
"semver": "~1.1.0",
"slide": "~1.1.3",
"license": "BSD",
"readme": "# npm-registry-client\n\nThe code that npm uses to talk to the registry.\n\nIt handles all the caching and HTTP calls.\n\n## Usage\n\n```javascript\nvar RegClient = require('npm-registry-client')\nvar client = new RegClient(config)\n\nclient.get(\"npm\", \"latest\", 1000, function (er, data, raw, res) {\n // error is an error if there was a problem.\n // data is the parsed data object\n // raw is the json string\n // res is the response from couch\n})\n```\n\n# Configuration\n\nThis program is designed to work with\n[npmconf](https://npmjs.org/package/npmconf), but you can also pass in\na plain-jane object with the appropriate configs, and it'll shim it\nfor you. Any configuration thingie that has get/set/del methods will\nalso be accepted.\n\n* `registry` **Required** {String} URL to the registry\n* `cache` **Required** {String} Path to the cache folder\n* `always-auth` {Boolean} Auth even for GET requests.\n* `auth` {String} A base64-encoded `username:password`\n* `email` {String} User's email address\n* `tag` {String} The default tag to use when publishing new packages.\n Default = `\"latest\"`\n* `ca` {String} Cerficate signing authority certificates to trust.\n* `strict-ssl` {Boolean} Whether or not to be strict with SSL\n certificates. Default = `true`\n* `user-agent` {String} User agent header to send. Default =\n `\"node/{process.version} {process.platform} {process.arch}\"`\n* `log` {Object} The logger to use. Defaults to `require(\"npmlog\")` if\n that works, otherwise logs are disabled.\n* `fetch-retries` {Number} Number of times to retry on GET failures.\n Default=2\n* `fetch-retry-factor` {Number} `factor` setting for `node-retry`. Default=10\n* `fetch-retry-mintimeout` {Number} `minTimeout` setting for `node-retry`.\n Default=10000 (10 seconds)\n* `fetch-retry-maxtimeout` {Number} `maxTimeout` setting for `node-retry`.\n Default=60000 (60 seconds)\n* `proxy` {URL} The url to proxy requests through.\n* `https-proxy` {URL} The url to proxy https requests through.\n Defaults to be the same as `proxy` if unset.\n* `_auth` {String} The base64-encoded authorization header.\n* `username` `_password` {String} Username/password to use to generate\n `_auth` if not supplied.\n* `_token` {Object} A token for use with\n [couch-login](https://npmjs.org/package/couch-login)\n\n# client.request(method, where, [what], [etag], [nofollow], cb)\n\n* `method` {String} HTTP method\n* `where` {String} Path to request on the server\n* `what` {Stream | Buffer | String | Object} The request body. Objects\n that are not Buffers or Streams are encoded as JSON.\n* `etag` {String} The cached ETag\n* `nofollow` {Boolean} Prevent following 302/301 responses\n* `cb` {Function}\n * `error` {Error | null}\n * `data` {Object} the parsed data object\n * `raw` {String} the json\n * `res` {Response Object} response from couch\n\nMake a request to the registry. All the other methods are wrappers\naround this. one.\n\n# client.adduser(username, password, email, cb)\n\n* `username` {String}\n* `password` {String}\n* `email` {String}\n* `cb` {Function}\n\nAdd a user account to the registry, or verify the credentials.\n\n# client.get(url, [timeout], [nofollow], [staleOk], cb)\n\n* `url` {String} The url path to fetch\n* `timeout` {Number} Number of seconds old that a cached copy must be\n before a new request will be made.\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `staleOk` {Boolean} If there's cached data available, then return that\n to the callback quickly, and update the cache the background.\n\nFetches data from the registry via a GET request, saving it in\nthe cache folder with the ETag.\n\n# client.publish(data, tarball, [readme], cb)\n\n* `data` {Object} Package data\n* `tarball` {String | Stream} Filename or stream of the package tarball\n* `readme` {String} Contents of the README markdown file\n* `cb` {Function}\n\nPublish a package to the registry.\n\nNote that this does not create the tarball from a folder. However, it\ncan accept a gzipped tar stream or a filename to a tarball.\n\n# client.star(package, starred, cb)\n\n* `package` {String} Name of the package to star\n* `starred` {Boolean} True to star the package, false to unstar it.\n* `cb` {Function}\n\nStar or unstar a package.\n\nNote that the user does not have to be the package owner to star or\nunstar a package, though other writes do require that the user be the\npackage owner.\n\n# client.stars(username, cb)\n\n* `username` {String} Name of user to fetch starred packages for.\n* `cb` {Function}\n\nView your own or another user's starred packages.\n\n# client.tag(project, version, tag, cb)\n\n* `project` {String} Project name\n* `version` {String} Version to tag\n* `tag` {String} Tag name to apply\n* `cb` {Function}\n\nMark a version in the `dist-tags` hash, so that `pkg@tag`\nwill fetch the specified version.\n\n# client.unpublish(name, [ver], cb)\n\n* `name` {String} package name\n* `ver` {String} version to unpublish. Leave blank to unpublish all\n versions.\n* `cb` {Function}\n\nRemove a version of a package (or all versions) from the registry. When\nthe last version us unpublished, the entire document is removed from the\ndatabase.\n\n# client.upload(where, file, [etag], [nofollow], cb)\n\n* `where` {String} URL path to upload to\n* `file` {String | Stream} Either the filename or a readable stream\n* `etag` {String} Cache ETag\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `cb` {Function}\n\nUpload an attachment. Mostly used by `client.publish()`.\n",
"readmeFilename": "README.md",
- "_id": "npm-registry-client@0.2.20",
- "_from": "npm-registry-client@~0.2.20"
+ "bugs": {
+ "url": "https://github.com/isaacs/npm-registry-client/issues"
+ },
+ "_id": "npm-registry-client@0.2.23",
+ "dist": {
+ "shasum": "a320ab2b1d048b4f7b88e40bd86974ca322b4c24"
+ },
+ "_from": "npm-registry-client@0.2.23",
+ "_resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-0.2.23.tgz"
}
--- /dev/null
+*.swp
+.*.swp
+
+.DS_Store
+*~
+.project
+.settings
+npm-debug.log
+coverage.html
+.idea
+lib-cov
+
+node_modules
\ No newline at end of file
--- /dev/null
+Copyright (c) Robert Kowalski
+All rights reserved.
+
+The BSD License
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
\ No newline at end of file
--- /dev/null
+## npmvalidate
+
+Validation for the npm client and npm-www (and probably other npm projects)
\ No newline at end of file
--- /dev/null
+exports.email = email
+exports.pw = pw
+exports.username = username
+
+function username (un) {
+ if (un !== un.toLowerCase()) {
+ return new Error('Username must be lowercase')
+ }
+
+ if (un !== encodeURIComponent(un)) {
+ return new Error('Username may not contain non-url-safe chars')
+ }
+
+ if (un.charAt(0) === '.') {
+ return new Error('Username may not start with "."')
+ }
+
+ return null
+}
+
+function email (em) {
+ if (!em.match(/^.+@.+\..+$/)) {
+ return new Error('Email must be an email address')
+ }
+
+ return null
+}
+
+function pw (pw) {
+ if (pw.match(/['!:@"]/)) {
+ return new Error('Sorry, passwords cannot contain these characters: \'!:@"')
+ }
+
+ return null
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "npm-user-validate",
+ "version": "0.0.1",
+ "description": "User validations for npm",
+ "main": "npm-user-validate.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "devDependencies": {
+ "tap": "0.4.1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/robertkowalski/npm-user-validate"
+ },
+ "keywords": [
+ "npm",
+ "validation"
+ ],
+ "author": {
+ "name": "Robert Kowalski",
+ "email": "rok@kowalski.gd"
+ },
+ "license": "BSD",
+ "readme": "## npmvalidate\n\nValidation for the npm client and npm-www (and probably other npm projects)",
+ "readmeFilename": "README.md",
+ "_id": "npm-user-validate@0.0.1",
+ "_from": "npm-user-validate@0"
+}
--- /dev/null
+var test = require('tap').test
+var v = require('../npm-user-validate.js').email
+
+test('email misses an @', function (t) {
+ err = v('namedomain')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('email misses a dot', function (t) {
+ err = v('name@domain')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('email misses a string before the @', function (t) {
+ err = v('@domain')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('email is ok', function (t) {
+ err = v('name@domain.com')
+ t.type(err, 'null')
+ t.end()
+})
\ No newline at end of file
--- /dev/null
+var test = require('tap').test
+var v = require('../npm-user-validate.js').pw
+
+test('pw contains a \'', function (t) {
+ err = v('\'')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('pw contains a :', function (t) {
+ err = v(':')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('pw contains a @', function (t) {
+ err = v('@')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('pw contains a "', function (t) {
+ err = v('"')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('pw is ok', function (t) {
+ err = v('duck')
+ t.type(err, 'null')
+ t.end()
+})
\ No newline at end of file
--- /dev/null
+var test = require('tap').test
+var v = require('../npm-user-validate.js').username
+
+test('username must be lowercase', function (t) {
+ err = v('ERRR')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('username may not contain non-url-safe chars', function (t) {
+ err = v('f ')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('username may not start with "."', function (t) {
+ err = v('.username')
+ t.type(err, 'object')
+ t.end()
+})
+
+test('username is ok', function (t) {
+ err = v('ente')
+ t.type(err, 'null')
+ t.end()
+})
, dd : ["--loglevel", "verbose"]
, ddd : ["--loglevel", "silly"]
, noreg : ["--no-registry"]
+ , N : ["--no-registry"]
, reg : ["--registry"]
, "no-reg" : ["--no-registry"]
, silent : ["--loglevel", "silent"]
-
var ProtoList = require('proto-list')
, path = require('path')
, fs = require('fs')
}
var json = exports.json = function () {
- var file = path.join.apply(null, [].slice.call(arguments))
+ var args = [].slice.call(arguments).filter(function (arg) { return arg != null })
+ var file = path.join.apply(null, args)
var content
try {
content = fs.readFileSync(file,'utf-8')
},
"readme": "A list of objects, bound by their prototype chain.\n\nUsed in npm's config stuff.\n",
"readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/isaacs/proto-list/issues"
+ },
"_id": "proto-list@1.2.2",
- "_from": "proto-list@~1.2.1"
+ "dist": {
+ "shasum": "48b88798261ec2c4a785720cdfec6200d57d3326"
+ },
+ "_from": "proto-list@~1.2.1",
+ "_resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.2.tgz"
}
{
"name": "config-chain",
- "version": "1.1.5",
+ "version": "1.1.7",
"description": "HANDLE CONFIGURATION ONCE AND FOR ALL",
"homepage": "http://github.com/dominictarr/config-chain",
"repository": {
},
"readme": "#config-chain\n\nUSE THIS MODULE TO LOAD ALL YOUR CONFIGURATIONS\n\n``` js\n\n //npm install config-chain\n\n var cc = require('config-chain')\n , opts = require('optimist').argv //ALWAYS USE OPTIMIST FOR COMMAND LINE OPTIONS.\n , env = opts.env || process.env.YOUR_APP_ENV || 'dev' //SET YOUR ENV LIKE THIS.\n\n // EACH ARG TO CONFIGURATOR IS LOADED INTO CONFIGURATION CHAIN\n // EARLIER ITEMS OVERIDE LATER ITEMS\n // PUTS COMMAND LINE OPTS FIRST, AND DEFAULTS LAST!\n\n //strings are interpereted as filenames.\n //will be loaded synchronously\n\n var conf =\n cc(\n //OVERRIDE SETTINGS WITH COMMAND LINE OPTS\n opts,\n\n //ENV VARS IF PREFIXED WITH 'myApp_'\n\n cc.env('myApp_'), //myApp_foo = 'like this'\n\n //FILE NAMED BY ENV\n path.join(__dirname, 'config.' + env + '.json'),\n\n //IF `env` is PRODUCTION\n env === 'prod'\n ? path.join(__dirname, 'special.json') //load a special file\n : null //NULL IS IGNORED!\n\n //SUBDIR FOR ENV CONFIG\n path.join(__dirname, 'config', env, 'config.json'),\n\n //SEARCH PARENT DIRECTORIES FROM CURRENT DIR FOR FILE\n cc.find('config.json'),\n\n //PUT DEFAULTS LAST\n {\n host: 'localhost'\n port: 8000\n })\n\n var host = conf.get('host')\n\n // or\n\n var host = conf.store.host\n\n```\n\nFINALLY, EASY FLEXIBLE CONFIGURATIONS!\n\n##see also: [proto-list](https://github.com/isaacs/proto-list/)\n\nWHATS THAT YOU SAY?\n\nYOU WANT A \"CLASS\" SO THAT YOU CAN DO CRAYCRAY JQUERY CRAPS?\n\nEXTEND WITH YOUR OWN FUNCTIONALTY!?\n\n## CONFIGCHAIN LIVES TO SERVE ONLY YOU!\n\n```javascript\nvar cc = require('config-chain')\n\n// all the stuff you did before\nvar config = cc({\n some: 'object'\n },\n cc.find('config.json'),\n cc.env('myApp_')\n )\n // CONFIGS AS A SERVICE, aka \"CaaS\", aka EVERY DEVOPS DREAM OMG!\n .addUrl('http://configurator:1234/my-configs')\n // ASYNC FTW!\n .addFile('/path/to/file.json')\n\n // OBJECTS ARE OK TOO, they're SYNC but they still ORDER RIGHT\n // BECAUSE PROMISES ARE USED BUT NO, NOT *THOSE* PROMISES, JUST\n // ACTUAL PROMISES LIKE YOU MAKE TO YOUR MOM, KEPT OUT OF LOVE\n .add({ another: 'object' })\n\n // DIE A THOUSAND DEATHS IF THIS EVER HAPPENS!!\n .on('error', function (er) {\n // IF ONLY THERE WAS SOMETHIGN HARDER THAN THROW\n // MY SORROW COULD BE ADEQUATELY EXPRESSED. /o\\\n throw er\n })\n\n // THROW A PARTY IN YOUR FACE WHEN ITS ALL LOADED!!\n .on('load', function (config) {\n console.awesome('HOLY SHIT!')\n })\n```\n\n# BORING API DOCS\n\n## cc(...args)\n\nMAKE A CHAIN AND ADD ALL THE ARGS.\n\nIf the arg is a STRING, then it shall be a JSON FILENAME.\n\nSYNC I/O!\n\nRETURN THE CHAIN!\n\n## cc.json(...args)\n\nJoin the args INTO A JSON FILENAME!\n\nSYNC I/O!\n\n## cc.find(relativePath)\n\nSEEK the RELATIVE PATH by climbing the TREE OF DIRECTORIES.\n\nRETURN THE FOUND PATH!\n\nSYNC I/O!\n\n## cc.parse(content, file, type)\n\nParse the content string, and guess the type from either the\nspecified type or the filename.\n\nRETURN THE RESULTING OBJECT!\n\nNO I/O!\n\n## cc.env(prefix, env=process.env)\n\nGet all the keys on the provided env object (or process.env) which are\nprefixed by the specified prefix, and put the values on a new object.\n\nRETURN THE RESULTING OBJECT!\n\nNO I/O!\n\n## cc.ConfigChain()\n\nThe ConfigChain class for CRAY CRAY JQUERY STYLE METHOD CHAINING!\n\nOne of these is returned by the main exported function, as well.\n\nIt inherits (prototypically) from\n[ProtoList](https://github.com/isaacs/proto-list/), and also inherits\n(parasitically) from\n[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)\n\nIt has all the methods from both, and except where noted, they are\nunchanged.\n\n### LET IT BE KNOWN THAT chain IS AN INSTANCE OF ConfigChain.\n\n## chain.sources\n\nA list of all the places where it got stuff. The keys are the names\npassed to addFile or addUrl etc, and the value is an object with some\ninfo about the data source.\n\n## chain.addFile(filename, type, [name=filename])\n\nFilename is the name of the file. Name is an arbitrary string to be\nused later if you desire. Type is either 'ini' or 'json', and will\ntry to guess intelligently if omitted.\n\nLoaded files can be saved later.\n\n## chain.addUrl(url, type, [name=url])\n\nSame as the filename thing, but with a url.\n\nCan't be saved later.\n\n## chain.addEnv(prefix, env, [name='env'])\n\nAdd all the keys from the env object that start with the prefix.\n\n## chain.addString(data, file, type, [name])\n\nParse the string and add it to the set. (Mainly used internally.)\n\n## chain.add(object, [name])\n\nAdd the object to the set.\n\n## chain.root {Object}\n\nThe root from which all the other config objects in the set descend\nprototypically.\n\nPut your defaults here.\n\n## chain.set(key, value, name)\n\nSet the key to the value on the named config object. If name is\nunset, then set it on the first config object in the set. (That is,\nthe one with the highest priority, which was added first.)\n\n## chain.get(key, [name])\n\nGet the key from the named config object explicitly, or from the\nresolved configs if not specified.\n\n## chain.save(name, type)\n\nWrite the named config object back to its origin.\n\nCurrently only supported for env and file config types.\n\nFor files, encode the data according to the type.\n\n## chain.on('save', function () {})\n\nWhen one or more files are saved, emits `save` event when they're all\nsaved.\n\n## chain.on('load', function (chain) {})\n\nWhen the config chain has loaded all the specified files and urls and\nsuch, the 'load' event fires.\n",
"readmeFilename": "readme.markdown",
- "_id": "config-chain@1.1.5",
- "_from": "config-chain@~1.1.1"
+ "bugs": {
+ "url": "https://github.com/dominictarr/config-chain/issues"
+ },
+ "_id": "config-chain@1.1.7",
+ "dist": {
+ "shasum": "e3c3e95a869b1aac2b565b18bd6274af1856d44c"
+ },
+ "_from": "config-chain@~1.1.1",
+ "_resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.7.tgz"
}
{
"name": "npmconf",
- "version": "0.0.23",
+ "version": "0.0.24",
"description": "The config thing npm uses",
"main": "npmconf.js",
"directories": {
"license": "BSD",
"readme": "# npmconf\n\nThe config thing npm uses\n\nIf you are interested in interacting with the config settings that npm\nuses, then use this module.\n\nHowever, if you are writing a new Node.js program, and want\nconfiguration functionality similar to what npm has, but for your\nown thing, then I'd recommend using [rc](https://github.com/dominictarr/rc),\nwhich is probably what you want.\n\nIf I were to do it all over again, that's what I'd do for npm. But,\nalas, there are many systems depending on many of the particulars of\nnpm's configuration setup, so it's not worth the cost of changing.\n\n## USAGE\n\n```javascript\nvar npmconf = require('npmconf')\n\n// pass in the cli options that you read from the cli\n// or whatever top-level configs you want npm to use for now.\nnpmconf.load({some:'configs'}, function (er, conf) {\n // do stuff with conf\n conf.get('some', 'cli') // 'configs'\n conf.get('username') // 'joebobwhatevers'\n conf.set('foo', 'bar', 'user')\n conf.save('user', function (er) {\n // foo = bar is now saved to ~/.npmrc or wherever\n })\n})\n```\n",
"readmeFilename": "README.md",
- "_id": "npmconf@0.0.23",
- "_from": "npmconf@0"
+ "bugs": {
+ "url": "https://github.com/isaacs/npmconf/issues"
+ },
+ "_id": "npmconf@0.0.24",
+ "_from": "npmconf@latest"
}
--- /dev/null
+/node_modules/
\ No newline at end of file
language: node_js
node_js:
- - 0.4
- - 0.6
+ - "0.10"
+ - "0.8"
\ No newline at end of file
--- /dev/null
+# Names sorted by how much code was originally theirs.
+Isaac Z. Schlueter <i@izs.me>
+Meryn Stol <merynstol@gmail.com>
\ No newline at end of file
--- /dev/null
+This package contains code originally written by Isaac Z. Schlueter.
+Used with permission.
+
+Copyright (c) Meryn Stol ("Author")
+All rights reserved.
+
+The BSD License
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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
+# normalize-package-data [![Build Status](https://travis-ci.org/meryn/normalize-package-data.png?branch=master)](https://travis-ci.org/meryn/normalize-package-data)
+
+normalize-package data exports a function that normalizes package metadata. This data is typically found in a package.json file, but in principle could come from any source - for example the npm registry.
+
+normalize-package-data is used by [read-package-json](https://npmjs.org/package/read-package-json) to normalize the data it reads from a package.json file. In turn, read-package-json is used by [npm](https://npmjs.org/package/npm) and various npm-related tools.
+
+## Installation
+
+```
+npm install normalize-package-data
+```
+
+## Usage
+
+Basic usage is really simple. You call the function that normalize-package-data exports. Let's call it `normalizeData`.
+
+```javascript
+normalizeData = require('normalize-package-data')
+packageData = fs.readfileSync("package.json")
+normalizeData(packageData)
+// packageData is now normalized
+```
+
+Optionally, you may pass a "warning" function. It gets called whenever the normalizeData function encounters something that doesn't look right. It indicates less than perfect input data.
+
+```javascript
+normalizeData = require('normalize-package-data')
+packageData = fs.readfileSync("package.json")
+warnFn = function(msg) { console.error(msg) }
+normalizeData(packageData, warnFn)
+// packageData is now normalized. Any number of warnings may have been logged.
+```
+
+If you don't provide a warning function, `normalizeData` functions silently.
+
+### Potential exceptions
+
+If the supplied data has an invalid name or version vield, `normalizeData` will throw an error. Depending on where you call `normalizeData`, you may want to catch these errors so can pass them to a callback.
+
+## What normalization (currently) entails
+
+* The value of `name` field gets trimmed.
+* The value of the `version` field gets cleaned by `semver.clean`. See [documentation for the semver module](https://github.com/isaacs/node-semver).
+* If `name` and/or `version` fields are missing, they are set to empty strings.
+* If `files` field is not an array, it will be removed.
+* If `bin` field is a string, then `bin` field will become an object with `name` set to the value of the `name` field, and `bin` set to the original string value.
+* If `man` field is a string, it will become an array with the original string as its sole member.
+* If `keywords` field is string, it is considered to be a list of keywords separated by one or more white-space characters. It gets converted to an array by splitting on `\s+`.
+* All people fields (`author`, `maintainers`, `contributors`) get converted into objects with name, email and url properties.
+* If `bundledDependencies` field (a typo) exists and `bundleDependencies` field does not, `bundledDependencies` will get renamed to `bundleDependencies`.
+* If the value of any of the dependencies fields (`dependencies`, `devDependencies`, `optionalDependencies`) is a string, it gets converted into an object with familiar `name=>value` pairs.
+* The values in `optionalDependencies` get added to `dependencies`. The `optionalDependencies` array is left untouched.
+* If `description` field does not exists, but `readme` field does, then (more or less) the first paragraph of text that's found in the readme is taken as value for `description`.
+* If `repository` field is a string, it will become an object with `url` set to the original string value, and `type` set to `"git"`.
+* If `bugs` field is a string, the value of `bugs` field is changed into an object with `url` set to the original string value.
+* If `bugs` field does not exist, but `repository` field points to a repository hosted on GitHub, the value of the `bugs` field gets set to an url in the form of https://github.com/[owner-name]/[repo-name]/issues . If the repository field points to a GitHub Gist repo url, the associated http url is chosen.
+* If `bugs` field is an object, the resulting value only has email and url properties. If email and url properties are not strings, they are ignored. If no valid values for either email or url is found, bugs field will be removed.
+* If `homepage` field is not a string, it will be removed.
+
+### Rules for name field
+
+If `name` field is given, the value of the name field must be a string. The string may not:
+
+* start with a period.
+* contain the following characters: `/@\s+%`
+* contain and characters that would need to be encoded for use in urls.
+* resemble the word `node_modules` or `favicon.ico` (case doesn't matter).
+
+### Rules for version field
+
+If `version` field is given, the value of the version field must be a valid *semver* string, as determined by the `semver.valid` method. See [documentation for the semver module](https://github.com/isaacs/node-semver).
+
+## Credits
+
+This package contains code based on read-package-json written by Isaac Z. Schlueter. Used with permisson.
+
+## License
+
+normalize-package-data is released under the [BSD 2-Clause License](http://opensource.org/licenses/MIT).
+Copyright (c) 2013 Meryn Stol
\ No newline at end of file
--- /dev/null
+module.exports = extractDescription
+
+// Extracts description from contents of a readme file in markdown format
+function extractDescription (d) {
+ if (!d) return;
+ // the first block of text before the first heading
+ // that isn't the first line heading
+ d = d.trim().split('\n')
+ for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s ++);
+ var l = d.length
+ for (var e = s + 1; e < l && d[e].trim(); e ++);
+ return d.slice(s, e).join(' ').trim()
+}
--- /dev/null
+var semver = require("semver")
+var parseGitHubURL = require("github-url-from-git")
+var depTypes = ["dependencies","devDependencies","optionalDependencies"]
+var extractDescription = require("./extract_description")
+var url = require("url")
+
+var fixer = module.exports = {
+ fixRepositoryField: function(data) {
+ if (data.repositories) {
+ this.warn("'repositories' (plural) Not supported.\n" +
+ "Please pick one as the 'repository' field");
+ data.repository = data.repositories[0]
+ }
+ if (!data.repository) return;
+ if (typeof data.repository === "string") {
+ data.repository = {
+ type: "git",
+ url: data.repository
+ }
+ }
+ var r = data.repository.url || ""
+ if (r) {
+ var ghurl = parseGitHubURL(r)
+ if (ghurl) {
+ r = ghurl.replace(/^https?:\/\//, 'git://')
+ }
+ }
+
+ if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) {
+ this.warn("Probably broken git url: " + r)
+ }
+ }
+
+, fixFilesField: function(data) {
+ var files = data.files
+ if (files && !Array.isArray(files)) {
+ this.warn("Invalid 'files' member")
+ delete data.files
+ }
+ }
+
+, fixBinField: function(data) {
+ if (!data.bin) return;
+ if (typeof data.bin === "string") {
+ var b = {}
+ b[data.name] = data.bin
+ data.bin = b
+ }
+ }
+
+, fixManField: function(data) {
+ if (!data.man) return;
+ if (typeof data.man === "string") {
+ data.man = [ data.man ]
+ }
+ }
+, fixBundleDependenciesField: function(data) {
+ var bdd = "bundledDependencies"
+ var bd = "bundleDependencies"
+ if (data[bdd] && !data[bd]) {
+ data[bd] = data[bdd]
+ delete data[bdd]
+ }
+ }
+
+, fixDependencies: function(data) {
+ objectifyDeps(data, this.warn)
+ addOptionalDepsToDeps(data, this.warn)
+ this.fixBundleDependenciesField(data)
+ }
+
+, fixKeywordsField: function (data, warn) {
+ if (typeof data.keywords === "string") {
+ data.keywords = data.keywords.split(/,\s+/)
+ }
+ }
+
+, fixVersionField: function(data) {
+ if (!data.version) {
+ data.version = ""
+ return true
+ }
+ if (!semver.valid(data.version)) {
+ throw new Error("invalid version: "+ data.version)
+ }
+ data.version = semver.clean(data.version)
+ return true
+ }
+
+, fixPeople: function(data) {
+ modifyPeople(data, unParsePerson)
+ modifyPeople(data, parsePerson)
+ }
+
+, fixNameField: function(data) {
+ if (!data.name) {
+ data.name = ""
+ return true
+ }
+ if (typeof data.name !== "string") {
+ throw new Error("name field must be a string.")
+ }
+ data.name = data.name.trim()
+ ensureValidName(data.name)
+ }
+
+
+, fixDescriptionField: function (data) {
+ if (data.description && typeof data.description !== 'string') {
+ this.warn("'description' field should be a string")
+ delete data.description
+ }
+ if (data.readme && !data.description)
+ data.description = extractDescription(data.readme)
+ }
+
+, fixReadmeField: function (data) {
+ if (!data.readme) data.readme = "ERROR: No README data found!"
+ }
+
+, fixBugsField: function(data) {
+ if (!data.bugs && data.repository && data.repository.url) {
+ var gh = parseGitHubURL(data.repository.url)
+ if(gh) {
+ if(gh.match(/^https:\/\/github.com\//))
+ data.bugs = {url: gh + "/issues"}
+ else // gist url
+ data.bugs = {url: gh}
+ }
+ }
+ else if(data.bugs) {
+ var emailRe = /^.+@.*\..+$/
+ if(typeof data.bugs == "string") {
+ if(emailRe.test(data.bugs))
+ data.bugs = {email:data.bugs}
+ else if(url.parse(data.bugs).protocol)
+ data.bugs = {url: data.bugs}
+ else
+ this.warn("Bug string field must be url, email, or {email,url}")
+ }
+ else {
+ var oldBugs = data.bugs
+ data.bugs = {}
+ if(oldBugs.url) {
+ if(typeof(oldBugs.url) == "string" && url.parse(oldBugs.url).protocol)
+ data.bugs.url = oldBugs.url
+ else
+ this.warn("bugs.url field must be a string url. Deleted.")
+ }
+ if(oldBugs.email) {
+ if(typeof(oldBugs.email) == "string" && emailRe.test(oldBugs.email))
+ data.bugs.email = oldBugs.email
+ else
+ this.warn("bugs.email field must be a string email. Deleted.")
+ }
+ }
+ if(!data.bugs.email && !data.bugs.url) {
+ delete data.bugs
+ this.warn("Normalized value of bugs field is an empty object. Deleted.")
+ }
+ }
+ }
+
+, fixHomepageField: function(data) {
+ if(!data.homepage) return true;
+ if(typeof data.homepage !== "string" || !url.parse(data.homepage).protocol) {
+ this.warn("homepage field must be a string url. Deleted.")
+ delete data.homepage
+ }
+ }
+}
+
+function ensureValidName (name) {
+ if (name.charAt(0) === "." ||
+ name.match(/[\/@\s\+%:]/) ||
+ name !== encodeURIComponent(name) ||
+ name.toLowerCase() === "node_modules" ||
+ name.toLowerCase() === "favicon.ico") {
+ throw new Error("Invalid name: " + JSON.stringify(name))
+ }
+}
+
+function modifyPeople (data, fn) {
+ if (data.author) data.author = fn(data.author)
+ ;["maintainers", "contributors"].forEach(function (set) {
+ if (!Array.isArray(data[set])) return;
+ data[set] = data[set].map(fn)
+ })
+ return data
+}
+
+function unParsePerson (person) {
+ if (typeof person === "string") return person
+ var name = person.name || ""
+ var u = person.url || person.web
+ var url = u ? (" ("+u+")") : ""
+ var e = person.email || person.mail
+ var email = e ? (" <"+e+">") : ""
+ return name+email+url
+}
+
+function parsePerson (person) {
+ if (typeof person !== "string") return person
+ var name = person.match(/^([^\(<]+)/)
+ var url = person.match(/\(([^\)]+)\)/)
+ var email = person.match(/<([^>]+)>/)
+ var obj = {}
+ if (name && name[0].trim()) obj.name = name[0].trim()
+ if (email) obj.email = email[1];
+ if (url) obj.url = url[1];
+ return obj
+}
+
+function addOptionalDepsToDeps (data, warn) {
+ var o = data.optionalDependencies
+ if (!o) return;
+ var d = data.dependencies || {}
+ Object.keys(o).forEach(function (k) {
+ d[k] = o[k]
+ })
+ data.dependencies = d
+}
+
+function depObjectify (deps) {
+ if (!deps) return {}
+ if (typeof deps === "string") {
+ deps = deps.trim().split(/[\n\r\s\t ,]+/)
+ }
+ if (!Array.isArray(deps)) return deps
+ var o = {}
+ deps.filter(function (d) {
+ return typeof d === "string"
+ }).forEach(function(d) {
+ d = d.trim().split(/(:?[@\s><=])/)
+ var dn = d.shift()
+ var dv = d.join("")
+ dv = dv.trim()
+ dv = dv.replace(/^@/, "")
+ o[dn] = dv
+ })
+ return o
+}
+
+function objectifyDeps (data, warn) {
+ depTypes.forEach(function (type) {
+ if (!data[type]) return;
+ data[type] = depObjectify(data[type])
+ })
+}
--- /dev/null
+// a warning for deprecated or likely-incorrect fields
+
+module.exports = isValid
+
+var typos = require("./typos")
+
+function isValid (data, warnFunc) {
+ var hasWarned = false
+ function warn(msg) {
+ hasWarned = true
+ if(warnFunc) warnFunc(msg)
+ }
+ if (data.modules) warn("'modules' is deprecated")
+ Object.keys(typos.topLevel).forEach(function (d) {
+ if (data.hasOwnProperty(d)) {
+ warn(makeTypoWarning(d, typos.topLevel[d]))
+ }
+ })
+ checkBugsField(data.bugs, warn)
+ checkScriptsField(data.scripts, warn)
+ if (!data.repository) warn("No repository field.")
+ if (!data.readme) warn("No readme data.")
+ if (data.description && typeof data.description !== 'string') {
+ warn("'description' field should be a string")
+ }
+ if (data[data.bundledDependencies] &&
+ !Array.isArray(data.bundleDependencies)) {
+ warn("bundleDependencies must be an array")
+ }
+ return !hasWarned
+}
+
+function checkBugsField (bugs, warn) {
+ if (!bugs || typeof bugs !== "object") return
+ Object.keys(bugs).forEach(function (k) {
+ if (typos.bugs[k]) {
+ bugs[typos.bugs[k]] = bugs[k]
+ delete bugs[k]
+ }
+ })
+}
+
+function checkScriptsField (scripts, warn) {
+ if (!scripts || typeof scripts !== "object") return
+ Object.keys(scripts).forEach(function (k) {
+ if (typos.script[k]) {
+ warn(makeTypoWarning(k, typos.script[k], "scripts"))
+ }
+ })
+}
+
+function makeTypoWarning (providedName, probableName, field) {
+ if (field) {
+ providedName = field + "['" + providedName + "']"
+ probableName = field + "['" + probableName + "']"
+ }
+ return providedName + " should probably be " + probableName + "."
+}
--- /dev/null
+module.exports = normalize
+
+var isValid = require("./is_valid")
+var fixer = require("./fixer")
+
+var fieldsToFix = ['name','version','description','repository'
+ ,'files','bin','man','bugs','keywords','readme','homepage']
+var otherThingsToFix = ['dependencies','people']
+
+var thingsToFix = fieldsToFix.map(function(fieldName) {
+ return ucFirst(fieldName) + "Field"
+})
+// two ways to do this in CoffeeScript on only one line, sub-70 chars:
+// thingsToFix = fieldsToFix.map (name) -> ucFirst(name) + "Field"
+// thingsToFix = (ucFirst(name) + "Field" for name in fieldsToFix)
+thingsToFix = thingsToFix.concat(otherThingsToFix)
+
+function normalize (data, warn) {
+ if(!warn) warn = function(msg) { /* noop */ }
+ isValid(data, warn) // don't care if it's valid, we'll make it valid
+ if (data.scripts &&
+ data.scripts.install === "node-gyp rebuild" &&
+ !data.scripts.preinstall) {
+ data.gypfile = true
+ }
+ fixer.warn = warn
+ thingsToFix.forEach(function(thingName) {
+ fixer["fix" + ucFirst(thingName)](data)
+ })
+ data._id = data.name + "@" + data.version
+ if (data.modules) delete data.modules // modules field is deprecated
+}
+
+function ucFirst (string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+}
--- /dev/null
+{
+ "topLevel": {
+ "dependancies": "dependencies"
+ ,"dependecies": "dependencies"
+ ,"depdenencies": "dependencies"
+ ,"devEependencies": "devDependencies"
+ ,"depends": "dependencies"
+ ,"dev-dependencies": "devDependencies"
+ ,"devDependences": "devDependencies"
+ ,"devDepenencies": "devDependencies"
+ ,"devdependencies": "devDependencies"
+ ,"repostitory": "repository"
+ ,"prefereGlobal": "preferGlobal"
+ ,"hompage": "homepage"
+ ,"hampage": "homepage"
+ ,"autohr": "author"
+ ,"autor": "author"
+ ,"contributers": "contributors"
+ ,"publicationConfig": "publishConfig"
+ },
+ "bugs": { "web": "url", "name": "url" },
+ "script": { "server": "start", "tests": "test" }
+}
\ No newline at end of file
--- /dev/null
+node_modules
--- /dev/null
+
+1.1.1 / 2013-04-23
+==================
+
+ * package.json: Move test stuff to devDeps
+
+1.1.0 / 2013-04-19
+==================
+
+ * Add support for gist urls
--- /dev/null
+
+test:
+ @./node_modules/.bin/mocha test.js --reporter spec --require should
+
+.PHONY: test
--- /dev/null
+
+# github-url-from-git
+
+```js
+describe('parse(url)', function(){
+ it('should support git://*', function(){
+ var url = 'git://github.com/jamesor/mongoose-versioner';
+ parse(url).should.equal('https://github.com/jamesor/mongoose-versioner');
+ })
+
+ it('should support git://*.git', function(){
+ var url = 'git://github.com/treygriffith/cellar.git';
+ parse(url).should.equal('https://github.com/treygriffith/cellar');
+ })
+
+ it('should support https://*', function(){
+ var url = 'https://github.com/Empeeric/i18n-node';
+ parse(url).should.equal('https://github.com/Empeeric/i18n-node');
+ })
+
+ it('should support https://*.git', function(){
+ var url = 'https://jpillora@github.com/banchee/tranquil.git';
+ parse(url).should.equal('https://github.com/banchee/tranquil');
+ })
+
+ it('should return undefined on failure', function(){
+ var url = 'git://github.com/justgord/.git';
+ assert(null == parse(url));
+ })
+
+ it('should parse git@gist urls', function() {
+ var url = 'git@gist.github.com:3135914.git';
+ parse(url).should.equal('https://gist.github.com/3135914')
+ })
+
+ it('should parse https://gist urls', function() {
+ var url = 'https://gist.github.com/3135914.git';
+ parse(url).should.equal('https://gist.github.com/3135914')
+ })
+})
+```
--- /dev/null
+var re = /^(?:https?:\/\/|git:\/\/)?(?:[^@]+@)?(gist.github.com|github.com)[:\/]([^\/]+\/[^\/]+?|[0-9]+)$/
+
+module.exports = function(url){
+ try {
+ var m = re.exec(url.replace(/\.git$/, ''));
+ var host = m[1];
+ var path = m[2];
+ return 'https://' + host + '/' + path;
+ } catch (err) {
+ // ignore
+ }
+};
--- /dev/null
+{
+ "name": "github-url-from-git",
+ "version": "1.1.1",
+ "description": "Parse a github git url and return the github repo url",
+ "main": "index.js",
+ "scripts": {
+ "test": "mocha test.js --reporter spec --require should"
+ },
+ "repository": "",
+ "keywords": [
+ "github",
+ "git",
+ "url",
+ "parser"
+ ],
+ "author": "",
+ "license": "MIT",
+ "devDependencies": {
+ "better-assert": "~1.0.0",
+ "mocha": "~1.9.0",
+ "should": "~1.2.2"
+ },
+ "readme": "\n# github-url-from-git\n\n```js\ndescribe('parse(url)', function(){\n it('should support git://*', function(){\n var url = 'git://github.com/jamesor/mongoose-versioner';\n parse(url).should.equal('https://github.com/jamesor/mongoose-versioner');\n })\n\n it('should support git://*.git', function(){\n var url = 'git://github.com/treygriffith/cellar.git';\n parse(url).should.equal('https://github.com/treygriffith/cellar');\n })\n\n it('should support https://*', function(){\n var url = 'https://github.com/Empeeric/i18n-node';\n parse(url).should.equal('https://github.com/Empeeric/i18n-node');\n })\n\n it('should support https://*.git', function(){\n var url = 'https://jpillora@github.com/banchee/tranquil.git';\n parse(url).should.equal('https://github.com/banchee/tranquil');\n })\n\n it('should return undefined on failure', function(){\n var url = 'git://github.com/justgord/.git';\n assert(null == parse(url));\n })\n\n it('should parse git@gist urls', function() {\n var url = 'git@gist.github.com:3135914.git';\n parse(url).should.equal('https://gist.github.com/3135914')\n })\n\n it('should parse https://gist urls', function() {\n var url = 'https://gist.github.com/3135914.git';\n parse(url).should.equal('https://gist.github.com/3135914')\n })\n})\n```\n",
+ "readmeFilename": "Readme.md",
+ "_id": "github-url-from-git@1.1.1",
+ "_from": "github-url-from-git@~1.1.1"
+}
--- /dev/null
+
+var parse = require('./');
+var assert = require('better-assert');
+
+describe('parse(url)', function(){
+ it('should support git://*', function(){
+ var url = 'git://github.com/jamesor/mongoose-versioner';
+ parse(url).should.equal('https://github.com/jamesor/mongoose-versioner');
+ })
+
+ it('should support git://*.git', function(){
+ var url = 'git://github.com/treygriffith/cellar.git';
+ parse(url).should.equal('https://github.com/treygriffith/cellar');
+ })
+
+ it('should support https://*', function(){
+ var url = 'https://github.com/Empeeric/i18n-node';
+ parse(url).should.equal('https://github.com/Empeeric/i18n-node');
+ })
+
+ it('should support https://*.git', function(){
+ var url = 'https://jpillora@github.com/banchee/tranquil.git';
+ parse(url).should.equal('https://github.com/banchee/tranquil');
+ })
+
+ it('should return undefined on failure', function(){
+ var url = 'git://github.com/justgord/.git';
+ assert(null == parse(url));
+ })
+
+ it('should parse git@gist urls', function() {
+ var url = 'git@gist.github.com:3135914.git';
+ parse(url).should.equal('https://gist.github.com/3135914')
+ })
+
+ it('should parse https://gist urls', function() {
+ var url = 'https://gist.github.com/3135914.git';
+ parse(url).should.equal('https://gist.github.com/3135914')
+ })
+})
--- /dev/null
+{
+ "name": "normalize-package-data",
+ "version": "0.1.6",
+ "author": {
+ "name": "Meryn Stol",
+ "email": "merynstol@gmail.com"
+ },
+ "description": "Normalizes data that can be found in package.json files.",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/meryn/normalize-package-data.git"
+ },
+ "main": "lib/normalize.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "semver": "1.x",
+ "github-url-from-git": "~1.1.1"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5",
+ "underscore": "~1.4.4",
+ "async": "~0.2.7"
+ },
+ "contributors": [
+ {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me"
+ },
+ {
+ "name": "Meryn Stol",
+ "email": "merynstol@gmail.com"
+ }
+ ],
+ "readme": "# normalize-package-data [![Build Status](https://travis-ci.org/meryn/normalize-package-data.png?branch=master)](https://travis-ci.org/meryn/normalize-package-data)\n\nnormalize-package data exports a function that normalizes package metadata. This data is typically found in a package.json file, but in principle could come from any source - for example the npm registry.\n\nnormalize-package-data is used by [read-package-json](https://npmjs.org/package/read-package-json) to normalize the data it reads from a package.json file. In turn, read-package-json is used by [npm](https://npmjs.org/package/npm) and various npm-related tools.\n\n## Installation\n\n```\nnpm install normalize-package-data\n```\n\n## Usage\n\nBasic usage is really simple. You call the function that normalize-package-data exports. Let's call it `normalizeData`.\n\n```javascript\nnormalizeData = require('normalize-package-data')\npackageData = fs.readfileSync(\"package.json\")\nnormalizeData(packageData)\n// packageData is now normalized\n```\n\nOptionally, you may pass a \"warning\" function. It gets called whenever the normalizeData function encounters something that doesn't look right. It indicates less than perfect input data.\n\n```javascript\nnormalizeData = require('normalize-package-data')\npackageData = fs.readfileSync(\"package.json\")\nwarnFn = function(msg) { console.error(msg) }\nnormalizeData(packageData, warnFn)\n// packageData is now normalized. Any number of warnings may have been logged.\n```\n\nIf you don't provide a warning function, `normalizeData` functions silently.\n\n### Potential exceptions\n\nIf the supplied data has an invalid name or version vield, `normalizeData` will throw an error. Depending on where you call `normalizeData`, you may want to catch these errors so can pass them to a callback.\n\n## What normalization (currently) entails\n\n* The value of `name` field gets trimmed.\n* The value of the `version` field gets cleaned by `semver.clean`. See [documentation for the semver module](https://github.com/isaacs/node-semver).\n* If `name` and/or `version` fields are missing, they are set to empty strings.\n* If `files` field is not an array, it will be removed.\n* If `bin` field is a string, then `bin` field will become an object with `name` set to the value of the `name` field, and `bin` set to the original string value.\n* If `man` field is a string, it will become an array with the original string as its sole member.\n* If `keywords` field is string, it is considered to be a list of keywords separated by one or more white-space characters. It gets converted to an array by splitting on `\\s+`.\n* All people fields (`author`, `maintainers`, `contributors`) get converted into objects with name, email and url properties.\n* If `bundledDependencies` field (a typo) exists and `bundleDependencies` field does not, `bundledDependencies` will get renamed to `bundleDependencies`.\n* If the value of any of the dependencies fields (`dependencies`, `devDependencies`, `optionalDependencies`) is a string, it gets converted into an object with familiar `name=>value` pairs.\n* The values in `optionalDependencies` get added to `dependencies`. The `optionalDependencies` array is left untouched.\n* If `description` field does not exists, but `readme` field does, then (more or less) the first paragraph of text that's found in the readme is taken as value for `description`.\n* If `repository` field is a string, it will become an object with `url` set to the original string value, and `type` set to `\"git\"`.\n* If `bugs` field is a string, the value of `bugs` field is changed into an object with `url` set to the original string value.\n* If `bugs` field does not exist, but `repository` field points to a repository hosted on GitHub, the value of the `bugs` field gets set to an url in the form of https://github.com/[owner-name]/[repo-name]/issues . If the repository field points to a GitHub Gist repo url, the associated http url is chosen.\n* If `bugs` field is an object, the resulting value only has email and url properties. If email and url properties are not strings, they are ignored. If no valid values for either email or url is found, bugs field will be removed.\n* If `homepage` field is not a string, it will be removed.\n\n### Rules for name field\n\nIf `name` field is given, the value of the name field must be a string. The string may not:\n\n* start with a period.\n* contain the following characters: `/@\\s+%`\n* contain and characters that would need to be encoded for use in urls.\n* resemble the word `node_modules` or `favicon.ico` (case doesn't matter).\n\n### Rules for version field\n\nIf `version` field is given, the value of the version field must be a valid *semver* string, as determined by the `semver.valid` method. See [documentation for the semver module](https://github.com/isaacs/node-semver).\n\n## Credits\n\nThis package contains code based on read-package-json written by Isaac Z. Schlueter. Used with permisson.\n\n## License\n\nnormalize-package-data is released under the [BSD 2-Clause License](http://opensource.org/licenses/MIT). \nCopyright (c) 2013 Meryn Stol ",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/meryn/normalize-package-data/issues"
+ },
+ "_id": "normalize-package-data@0.1.6",
+ "_from": "normalize-package-data@~0.1.2"
+}
--- /dev/null
+var tap = require("tap")
+var normalize = require("../lib/normalize")
+var path = require("path")
+var fs = require("fs")
+
+tap.test("basic test", function (t) {
+ var p = path.resolve(__dirname, "./fixtures/read-package-json.json")
+ fs.readFile (p, function (err, contents) {
+ if (err) throw err;
+ var originalData = JSON.parse(contents.toString())
+ var data = JSON.parse(contents.toString())
+ normalize(data)
+ t.ok(data)
+ verifyFields(t, data, originalData)
+ t.end()
+ })
+})
+
+function verifyFields (t, normalized, original) {
+ t.equal(normalized.version, original.version, "Version field stays same")
+ t.equal(normalized._id, normalized.name + "@" + normalized.version, "It gets good id.")
+ t.equal(normalized.name, original.name, "Name stays the same.")
+ t.type(normalized.author, "object", "author field becomes object")
+ t.deepEqual(normalized.scripts, original.scripts, "scripts field (object) stays same")
+ t.equal(normalized.main, original.main)
+ // optional deps are folded in.
+ t.deepEqual(normalized.optionalDependencies,
+ original.optionalDependencies)
+ t.has(normalized.dependencies, original.optionalDependencies, "opt depedencies are copied into dependencies")
+ t.has(normalized.dependencies, original.dependencies, "regular depedencies stay in place")
+ t.deepEqual(normalized.devDependencies, original.devDependencies)
+ t.type(normalized.bugs, "object", "bugs should become object")
+ t.equal(normalized.bugs.url, "https://github.com/isaacs/read-package-json/issues")
+}
--- /dev/null
+var tap = require("tap")
+var normalize = require("../lib/normalize")
+var path = require("path")
+var fs = require("fs")
+var _ = require("underscore")
+var async = require("async")
+
+var data, clonedData
+var warn
+
+tap.test("consistent normalization", function(t) {
+ path.resolve(__dirname, "./fixtures/read-package-json.json")
+ fs.readdir (__dirname + "/fixtures", function (err, entries) {
+ // entries = ['coffee-script.json'] // uncomment to limit to a specific file
+ verifyConsistency = function(entryName, next) {
+ warn = function(msg) {
+ // t.equal("",msg) // uncomment to have some kind of logging of warnings
+ }
+ filename = __dirname + "/fixtures/" + entryName
+ fs.readFile(filename, function(err, contents) {
+ if (err) return next(err)
+ data = JSON.parse(contents.toString())
+ normalize(data, warn)
+ clonedData = _.clone(data)
+ normalize(data, warn)
+ t.deepEqual(clonedData, data,
+ "Normalization of " + entryName + "is consistent.")
+ next(null)
+ }) // fs.readFile
+ } // verifyConsistency
+ async.forEach(entries, verifyConsistency, function(err) {
+ if (err) throw err
+ t.end()
+ })
+ }) // fs.readdir
+}) // tap.test
\ No newline at end of file
--- /dev/null
+{
+ "name": "async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "main": "./lib/async",
+ "author": "Caolan McMahon",
+ "version": "0.2.6",
+ "repository" : {
+ "type" : "git",
+ "url" : "http://github.com/caolan/async.git"
+ },
+ "bugs" : {
+ "url" : "http://github.com/caolan/async/issues"
+ },
+ "licenses" : [
+ {
+ "type" : "MIT",
+ "url" : "http://github.com/caolan/async/raw/master/LICENSE"
+ }
+ ],
+ "devDependencies": {
+ "nodeunit": ">0.0.0",
+ "uglify-js": "1.2.x",
+ "nodelint": ">0.0.0"
+ },
+ "jam": {
+ "main": "lib/async.js",
+ "include": [
+ "lib/async.js",
+ "README.md",
+ "LICENSE"
+ ]
+ },
+ "scripts": {
+ "test": "nodeunit test/test-async.js"
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "bcrypt",
+ "description": "A bcrypt library for NodeJS.",
+ "keywords": [
+ "bcrypt",
+ "password",
+ "auth",
+ "authentication",
+ "encryption",
+ "crypt",
+ "crypto"
+ ],
+ "main": "./bcrypt",
+ "version": "0.7.5",
+ "author": "Nick Campbell (http://github.com/ncb000gt)",
+ "engines": {
+ "node": ">= 0.6.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/ncb000gt/node.bcrypt.js.git"
+ },
+ "licenses": [
+ {
+ "type": "MIT"
+ }
+ ],
+ "bugs": {
+ "url": "http://github.com/ncb000gt/node.bcrypt.js/issues"
+ },
+ "scripts": {
+ "test": "node-gyp configure build && nodeunit test"
+ },
+ "dependencies": {
+ "bindings": "1.0.0"
+ },
+ "devDependencies": {
+ "nodeunit": ">=0.6.4"
+ },
+ "contributors": [
+ "Antonio Salazar Cardozo <savedfastcool@gmail.com> (https://github.com/Shadowfiend)",
+ "Van Nguyen <the.gol.effect@gmail.com> (https://github.com/thegoleffect)",
+ "David Trejo <david@dtrejo.com> (https://github.com/dtrejo)",
+ "Ben Glow <glen.low@pixelglow.com> (https://github.com/pixelglow)",
+ "NewITFarmer.com <> (https://github.com/newitfarmer)",
+ "Alfred Westerveld <alfredwesterveld@gmail.com> (https://github.com/alfredwesterveld)",
+ "Vincent Côté-Roy <vincentcr@gmail.com> (https://github.com/vincentcr)",
+ "Lloyd Hilaiel <lloyd@hilaiel.com> (https://github.com/lloyd)",
+ "Roman Shtylman <shtylman@gmail.com> (https://github.com/shtylman)",
+ "Vadim Graboys <dimva13@gmail.com> (https://github.com/vadimg)",
+ "Ben Noorduis <> (https://github.com/bnoordhuis)",
+ "Nate Rajlich <nathan@tootallnate.net> (https://github.com/tootallnate)",
+ "Sean McArthur <sean.monstar@gmail.com> (https://github.com/seanmonstar)",
+ "Fanie Oosthuysen <fanie.oosthuysen@gmail.com> (https://github.com/weareu)"
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "coffee-script",
+ "description": "Unfancy JavaScript",
+ "keywords": ["javascript", "language", "coffeescript", "compiler"],
+ "author": "Jeremy Ashkenas",
+ "version": "1.6.2",
+ "licenses": [{
+ "type": "MIT",
+ "url": "https://raw.github.com/jashkenas/coffee-script/master/LICENSE"
+ }],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "directories" : {
+ "lib" : "./lib/coffee-script"
+ },
+ "main" : "./lib/coffee-script/coffee-script",
+ "bin": {
+ "coffee": "./bin/coffee",
+ "cake": "./bin/cake"
+ },
+ "scripts": {
+ "test": "node ./bin/cake test"
+ },
+ "homepage": "http://coffeescript.org",
+ "bugs": "https://github.com/jashkenas/coffee-script/issues",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/jashkenas/coffee-script.git"
+ },
+ "devDependencies": {
+ "uglify-js": "~2.2",
+ "jison": ">=0.2.0"
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "http-server",
+ "preferGlobal": true,
+ "version": "0.3.0",
+ "author": "Nodejitsu <support@nodejitsu.com>",
+ "description": "a simple zero-configuration command-line http server",
+ "contributors": [
+ {
+ "name": "Marak Squires",
+ "email": "marak@nodejitsu.com"
+ }
+ ],
+ "bin": {
+ "http-server": "./bin/http-server"
+ },
+ "scripts": {
+ "start": "node ./bin/http-server",
+ "test": "vows --spec --isolate",
+ "predeploy": "echo This will be run before deploying the app",
+ "postdeploy": "echo This will be run after deploying the app"
+ },
+ "main": "./lib/http-server",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nodejitsu/http-server.git"
+ },
+ "keywords": [
+ "cli",
+ "http",
+ "server"
+ ],
+ "dependencies" : {
+ "colors" : "*",
+ "flatiron" : "0.1.x",
+ "optimist" : "0.2.x",
+ "union" : "0.1.x",
+ "ecstatic" : "0.1.x",
+ "plates" : "https://github.com/flatiron/plates/tarball/master"
+ },
+ "analyze": false,
+ "devDependencies": {
+ "vows" : "0.5.x",
+ "request" : "2.1.x"
+ },
+ "bundledDependencies": [
+ "union",
+ "ecstatic"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "movefile",
+ "description": "rename implementation working over devices",
+ "version": "0.2.0",
+ "author": "yazgazan <yazgazan@gmail.com>",
+ "main": "./build/Release/movefile",
+ "keywords": ["move", "file", "rename"],
+ "repository": "git://github.com/yazgazan/movefile.git",
+ "directories": {
+ "lib": "./build/Release/"
+ },
+ "scripts": {
+ "install": "./node_modules/node-gyp/bin/node-gyp.js configure && ./node_modules/node-gyp/bin/node-gyp.js build"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "dependencies": {
+ "node-gyp": "~0.9.1"
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "node-module_exist",
+ "description": "Find if a NodeJS module is available to require or not",
+ "version": "0.0.1",
+ "main": "module_exist.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@gist.github.com:3135914.git"
+ },
+ "homepage": "https://github.com/FGRibreau",
+ "author": {
+ "name": "Francois-Guillaume Ribreau",
+ "url": "http://fgribreau.com.com/"
+ },
+ "devDependencies": {
+ "nodeunit": "~0.7.4"
+ },
+ "keywords": [
+ "core",
+ "modules"
+ ],
+ "license": "MIT"
+}
\ No newline at end of file
--- /dev/null
+{
+ "version": "1.2.17",
+ "name": "npm",
+ "publishConfig": {
+ "proprietary-attribs": false
+ },
+ "description": "A package manager for node",
+ "keywords": [
+ "package manager",
+ "modules",
+ "install",
+ "package.json"
+ ],
+ "preferGlobal": true,
+ "config": {
+ "publishtest": false
+ },
+ "homepage": "https://npmjs.org/doc/",
+ "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/isaacs/npm"
+ },
+ "bugs": {
+ "email": "npm-@googlegroups.com",
+ "url": "http://github.com/isaacs/npm/issues"
+ },
+ "directories": {
+ "doc": "./doc",
+ "man": "./man",
+ "lib": "./lib",
+ "bin": "./bin"
+ },
+ "main": "./lib/npm.js",
+ "bin": "./bin/npm-cli.js",
+ "dependencies": {
+ "semver": "~1.1.2",
+ "ini": "~1.1.0",
+ "slide": "1",
+ "abbrev": "~1.0.4",
+ "graceful-fs": "~1.2.0",
+ "minimatch": "~0.2.11",
+ "nopt": "~2.1.1",
+ "rimraf": "2",
+ "request": "~2.9",
+ "which": "1",
+ "tar": "~0.1.17",
+ "fstream": "~0.1.22",
+ "block-stream": "*",
+ "inherits": "1",
+ "mkdirp": "~0.3.3",
+ "read": "~1.0.4",
+ "lru-cache": "~2.3.0",
+ "node-gyp": "~0.9.3",
+ "fstream-npm": "~0.1.3",
+ "uid-number": "0",
+ "archy": "0",
+ "chownr": "0",
+ "npmlog": "0",
+ "ansi": "~0.1.2",
+ "npm-registry-client": "~0.2.18",
+ "read-package-json": "~0.3.0",
+ "read-installed": "0",
+ "glob": "~3.1.21",
+ "init-package-json": "0.0.6",
+ "osenv": "0",
+ "lockfile": "~0.3.0",
+ "retry": "~0.6.0",
+ "once": "~1.1.1",
+ "npmconf": "0",
+ "opener": "~1.3.0",
+ "chmodr": "~0.1.0",
+ "cmd-shim": "~1.1.0"
+ },
+ "bundleDependencies": [
+ "semver",
+ "ini",
+ "slide",
+ "abbrev",
+ "graceful-fs",
+ "minimatch",
+ "nopt",
+ "rimraf",
+ "request",
+ "which",
+ "tar",
+ "fstream",
+ "block-stream",
+ "inherits",
+ "mkdirp",
+ "read",
+ "lru-cache",
+ "node-gyp",
+ "fstream-npm",
+ "uid-number",
+ "archy",
+ "chownr",
+ "npmlog",
+ "ansi",
+ "npm-registry-client",
+ "read-package-json",
+ "read-installed",
+ "glob",
+ "init-package-json",
+ "osenv",
+ "lockfile",
+ "retry",
+ "once",
+ "npmconf",
+ "opener",
+ "chmodr",
+ "cmd-shim"
+ ],
+ "devDependencies": {
+ "ronn": "~0.3.6",
+ "tap": "~0.4.0"
+ },
+ "engines": {
+ "node": ">=0.6",
+ "npm": "1"
+ },
+ "scripts": {
+ "test": "node ./test/run.js && tap test/tap/*.js",
+ "tap": "tap test/tap/*.js",
+ "prepublish": "node bin/npm-cli.js prune ; rm -rf test/*/*/node_modules ; make -j4 doc",
+ "dumpconf": "env | grep npm | sort | uniq",
+ "echo": "node bin/npm-cli.js"
+ },
+ "licenses": [
+ {
+ "type": "MIT +no-false-attribs",
+ "url": "https://github.com/isaacs/npm/raw/master/LICENSE"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "read-package-json",
+ "version": "0.1.1",
+ "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
+ "description": "The thing npm uses to read package.json files with semantics and defaults and validation",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/read-package-json.git"
+ },
+ "main": "read-json.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "glob": "~3.1.9",
+ "lru-cache": "~1.1.0",
+ "semver": "~1.0.14",
+ "slide": "~1.1.3"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "optionalDependencies": {
+ "npmlog": "0",
+ "graceful-fs": "~1.1.8"
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "request",
+ "description": "Simplified HTTP request client.",
+ "tags": [
+ "http",
+ "simple",
+ "util",
+ "utility"
+ ],
+ "version": "2.16.7",
+ "author": "Mikeal Rogers <mikeal.rogers@gmail.com>",
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/mikeal/request.git"
+ },
+ "bugs": {
+ "url": "http://github.com/mikeal/request/issues"
+ },
+ "engines": [
+ "node >= 0.8.0"
+ ],
+ "main": "index.js",
+ "dependencies": {
+ "form-data": "~0.0.3",
+ "mime": "~1.2.7",
+ "hawk": "~0.10.2",
+ "node-uuid": "~1.4.0",
+ "cookie-jar": "~0.2.0",
+ "aws-sign": "~0.2.0",
+ "oauth-sign": "~0.2.0",
+ "forever-agent": "~0.2.0",
+ "tunnel-agent": "~0.2.0",
+ "json-stringify-safe": "~3.0.0",
+ "qs": "~0.5.4"
+ },
+ "scripts": {
+ "test": "node tests/run.js"
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name" : "underscore",
+ "description" : "JavaScript's functional programming helper library.",
+ "homepage" : "http://underscorejs.org",
+ "keywords" : ["util", "functional", "server", "client", "browser"],
+ "author" : "Jeremy Ashkenas <jeremy@documentcloud.org>",
+ "repository" : {"type": "git", "url": "git://github.com/documentcloud/underscore.git"},
+ "main" : "underscore.js",
+ "version" : "1.4.4",
+ "devDependencies": {
+ "phantomjs": "1.9.0-1"
+ },
+ "scripts": {
+ "test": "phantomjs test/vendor/runner.js test/index.html?noglobals=true"
+ },
+ "license" : "MIT"
+}
\ No newline at end of file
--- /dev/null
+var tap = require("tap")
+var normalize = require("../lib/normalize")
+var path = require("path")
+var fs = require("fs")
+var _ = require("underscore")
+var async = require("async")
+
+var data, clonedData
+var warn
+
+tap.test("consistent normalization", function(t) {
+ entries = [
+ 'read-package-json.json',
+ 'http-server.json',
+ "movefile.json",
+ "node-module_exist.json"
+ ]
+ verifyConsistency = function(entryName, next) {
+ warn = function(msg) {
+ // t.equal("",msg) // uncomment to have some kind of logging of warnings
+ }
+ filename = __dirname + "/fixtures/" + entryName
+ fs.readFile(filename, function(err, contents) {
+ if (err) return next(err)
+ data = JSON.parse(contents.toString())
+ normalize(data, warn)
+ if(data.name == "node-module_exist") {
+ t.same(data.bugs.url, "https://gist.github.com/3135914")
+ }
+ if(data.name == "read-package-json") {
+ t.same(data.bugs.url, "https://github.com/isaacs/read-package-json/issues")
+ }
+ if(data.name == "http-server") {
+ t.same(data.bugs.url, "https://github.com/nodejitsu/http-server/issues")
+ }
+ if(data.name == "movefile") {
+ t.same(data.bugs.url, "https://github.com/yazgazan/movefile/issues")
+ }
+ next(null)
+ }) // fs.readFile
+ } // verifyConsistency
+ async.forEach(entries, verifyConsistency, function(err) {
+ if (err) throw err
+ t.end()
+ })
+}) // tap.test
\ No newline at end of file
--- /dev/null
+var tap = require("tap")
+var fs = require("fs")
+var path = require("path")
+
+var globals = Object.keys(global)
+
+var normalize = require("../lib/normalize")
+
+var rpjPath = path.resolve(__dirname,"./fixtures/read-package-json.json")
+tap.test("normalize some package data", function(t) {
+ var packageData = require(rpjPath)
+ var warnings = []
+ normalize(packageData, function(warning) {
+ warnings.push(warning)
+ })
+ // there's no readme data in this particular object
+ t.equal( warnings.length, 1, "There's exactly one warning.")
+ fs.readFile(rpjPath, function(err, data) {
+ if(err) throw err
+ // Various changes have been made
+ t.notEqual(packageData, JSON.parse(data), "Output is different from input.")
+ t.end()
+ })
+})
+
+tap.test("runs without passing warning function", function(t) {
+ var packageData = require(rpjPath)
+ fs.readFile(rpjPath, function(err, data) {
+ if(err) throw err
+ normalize(JSON.parse(data))
+ t.ok(true, "If you read this, this means I'm still alive.")
+ t.end()
+ })
+})
+
+tap.test("empty object", function(t) {
+ var packageData = {}
+ var expect =
+ { name: '',
+ version: '',
+ readme: 'ERROR: No README data found!',
+ _id: '@' }
+
+ var warnings = []
+ function warn(m) {
+ warnings.push(m)
+ }
+ normalize(packageData, warn)
+ t.same(packageData, expect)
+ t.same(warnings, ["No repository field.","No readme data."])
+ t.end()
+})
+
+tap.test("urls required", function(t) {
+ var warnings = []
+ function warn(w) {
+ warnings.push(w)
+ }
+ normalize({
+ bugs: {
+ url: "/1",
+ email: "not an email address"
+ }
+ }, warn)
+ var a
+ normalize(a={
+ readme: "read yourself how about",
+ homepage: "stragle planarf",
+ bugs: "what is this i don't even",
+ repository: "Hello."
+ }, warn)
+
+ console.error(a)
+
+ var expect =
+ [ 'No repository field.',
+ 'No readme data.',
+ 'bugs.url field must be a string url. Deleted.',
+ 'bugs.email field must be a string email. Deleted.',
+ 'Normalized value of bugs field is an empty object. Deleted.',
+ 'Bug string field must be url, email, or {email,url}',
+ 'Normalized value of bugs field is an empty object. Deleted.',
+ 'homepage field must be a string url. Deleted.' ]
+ t.same(warnings, expect)
+ t.end()
+})
+
+tap.test("gist bugs url", function(t) {
+ var d = {
+ repository: "git@gist.github.com:123456.git"
+ }
+ normalize(d)
+ t.same(d.repository, { type: 'git', url: 'git@gist.github.com:123456.git' })
+ t.same(d.bugs, { url: 'https://gist.github.com/123456' })
+ t.end();
+});
+
+tap.test('no new globals', function(t) {
+ t.same(Object.keys(global), globals)
+ t.end()
+})
+
+tap.test("singularize repositories", function(t) {
+ d = {repositories:["git@gist.github.com:123456.git"]}
+ normalize(d)
+ t.same(d.repository, { type: 'git', url: 'git@gist.github.com:123456.git' })
+ t.end()
+});
\ No newline at end of file
--- /dev/null
+var test = require('tap').test
+var normalize = require('../')
+var typos = require('../lib/typos.json')
+
+test('typos', function(t) {
+ var warnings = []
+ function warn(m) {
+ warnings.push(m)
+ }
+
+ var expect =
+ [ 'dependancies should probably be dependencies.',
+ 'dependecies should probably be dependencies.',
+ 'depdenencies should probably be dependencies.',
+ 'devEependencies should probably be devDependencies.',
+ 'depends should probably be dependencies.',
+ 'dev-dependencies should probably be devDependencies.',
+ 'devDependences should probably be devDependencies.',
+ 'devDepenencies should probably be devDependencies.',
+ 'devdependencies should probably be devDependencies.',
+ 'repostitory should probably be repository.',
+ 'prefereGlobal should probably be preferGlobal.',
+ 'hompage should probably be homepage.',
+ 'hampage should probably be homepage.',
+ 'autohr should probably be author.',
+ 'autor should probably be author.',
+ 'contributers should probably be contributors.',
+ 'publicationConfig should probably be publishConfig.',
+ 'No repository field.',
+ 'No repository field.',
+ 'No readme data.',
+ 'bugs.url field must be a string url. Deleted.',
+ 'Normalized value of bugs field is an empty object. Deleted.',
+ 'No repository field.',
+ 'No readme data.' ]
+
+ normalize({"dependancies": "dependencies"
+ ,"dependecies": "dependencies"
+ ,"depdenencies": "dependencies"
+ ,"devEependencies": "devDependencies"
+ ,"depends": "dependencies"
+ ,"dev-dependencies": "devDependencies"
+ ,"devDependences": "devDependencies"
+ ,"devDepenencies": "devDependencies"
+ ,"devdependencies": "devDependencies"
+ ,"repostitory": "repository"
+ ,"prefereGlobal": "preferGlobal"
+ ,"hompage": "homepage"
+ ,"hampage": "homepage"
+ ,"autohr": "author"
+ ,"autor": "author"
+ ,"contributers": "contributors"
+ ,"publicationConfig": "publishConfig"
+ ,readme:"asdf"
+ ,name:"name"
+ ,version:"1.2.5"}, warn)
+
+ normalize({name:"name"
+ ,version:"1.2.5"
+ ,bugs:{web:"url",name:"url"}}, warn)
+
+ normalize({name:"name"
+ ,version:"1.2.5"
+ ,script:{server:"start",tests:"test"}}, warn)
+ t.same(warnings, expect)
+ t.end();
+})
{
"name": "read-package-json",
- "version": "0.3.0",
+ "version": "0.4.1",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"test": "tap test/*.js"
},
"dependencies": {
- "glob": "~3.1.9",
+ "glob": "~3.2.1",
"lru-cache": "2",
- "semver": "1.x",
- "slide": "~1.1.3",
+ "normalize-package-data": "~0.1.2",
"npmlog": "0",
"graceful-fs": "~1.2"
},
},
"readme": "# read-package-json\n\nThis is the thing that npm uses to read package.json files. It\nvalidates some stuff, and loads some default things.\n\nIt keeps a cache of the files you've read, so that you don't end\nup reading the same package.json file multiple times.\n\nNote that if you just want to see what's literally in the package.json\nfile, you can usually do `var data = require('some-module/package.json')`.\n\nThis module is basically only needed by npm, but it's handy to see what\nnpm will see when it looks at your package.\n\n## Usage\n\n```javascript\nvar readJson = require('read-package-json')\n\nreadJson('/path/to/package.json', function (er, data) {\n if (er) {\n console.error(\"There was an error reading the file\")\n return\n }\n\n console.error('the package data is', data)\n}\n```\n\n## readJson(file, cb)\n\n* `file` {String} The path to the package.json file\n* `cb` {Function}\n\nReads the JSON file and does the things.\n\n## `package.json` Fields\n\nSee `man 5 package.json` or `npm help json`.\n\n## readJson.log\n\nBy default this is a reference to the `npmlog` module. But if that\nmodule can't be found, then it'll be set to just a dummy thing that does\nnothing.\n\nReplace with your own `{log,warn,error}` object for fun loggy time.\n\n## readJson.extras(file, data, cb)\n\nRun all the extra stuff relative to the file, with the parsed data.\n\nModifies the data as it does stuff. Calls the cb when it's done.\n\n## readJson.extraSet = [fn, fn, ...]\n\nArray of functions that are called by `extras`. Each one receives the\narguments `fn(file, data, cb)` and is expected to call `cb(er, data)`\nwhen done or when an error occurs.\n\nOrder is indeterminate, so each function should be completely\nindependent.\n\nMix and match!\n\n## readJson.cache\n\nThe `lru-cache` object that readJson uses to not read the same file over\nand over again. See\n[lru-cache](https://github.com/isaacs/node-lru-cache) for details.\n\n## Other Relevant Files Besides `package.json`\n\nSome other files have an effect on the resulting data object, in the\nfollowing ways:\n\n### `README?(.*)`\n\nIf there is a `README` or `README.*` file present, then npm will attach\na `readme` field to the data with the contents of this file.\n\nOwing to the fact that roughly 100% of existing node modules have\nMarkdown README files, it will generally be assumed to be Markdown,\nregardless of the extension. Please plan accordingly.\n\n### `server.js`\n\nIf there is a `server.js` file, and there is not already a\n`scripts.start` field, then `scripts.start` will be set to `node\nserver.js`.\n\n### `AUTHORS`\n\nIf there is not already a `contributors` field, then the `contributors`\nfield will be set to the contents of the `AUTHORS` file, split by lines,\nand parsed.\n\n### `bindings.gyp`\n\nIf a bindings.gyp file exists, and there is not already a\n`scripts.install` field, then the `scripts.install` field will be set to\n`node-gyp rebuild`.\n\n### `wscript`\n\nIf a wscript file exists, and there is not already a `scripts.install`\nfield, then the `scripts.install` field will be set to `node-waf clean ;\nnode-waf configure build`.\n\nNote that the `bindings.gyp` file supercedes this, since node-waf has\nbeen deprecated in favor of node-gyp.\n\n### `index.js`\n\nIf the json file does not exist, but there is a `index.js` file\npresent instead, and that file has a package comment, then it will try\nto parse the package comment, and use that as the data instead.\n\nA package comment looks like this:\n\n```javascript\n/**package\n * { \"name\": \"my-bare-module\"\n * , \"version\": \"1.2.3\"\n * , \"description\": \"etc....\" }\n **/\n\n// or...\n\n/**package\n{ \"name\": \"my-bare-module\"\n, \"version\": \"1.2.3\"\n, \"description\": \"etc....\" }\n**/\n```\n\nThe important thing is that it starts with `/**package`, and ends with\n`**/`. If the package.json file exists, then the index.js is not\nparsed.\n\n### `{directories.man}/*.[0-9]`\n\nIf there is not already a `man` field defined as an array of files or a\nsingle file, and\nthere is a `directories.man` field defined, then that directory will\nbe searched for manpages.\n\nAny valid manpages found in that directory will be assigned to the `man`\narray, and installed in the appropriate man directory at package install\ntime, when installed globally on a Unix system.\n\n### `{directories.bin}/*`\n\nIf there is not already a `bin` field defined as a string filename or a\nhash of `<name> : <filename>` pairs, then the `directories.bin`\ndirectory will be searched and all the files within it will be linked as\nexecutables at install time.\n\nWhen installing locally, npm links bins into `node_modules/.bin`, which\nis in the `PATH` environ when npm runs scripts. When\ninstalling globally, they are linked into `{prefix}/bin`, which is\npresumably in the `PATH` environment variable.\n",
"readmeFilename": "README.md",
- "_id": "read-package-json@0.3.0",
- "_from": "read-package-json@latest"
+ "bugs": {
+ "url": "https://github.com/isaacs/read-package-json/issues"
+ },
+ "_id": "read-package-json@0.4.1",
+ "_from": "read-package-json@~0.4.1"
}
readJson.cache = new LRU({max: 1000})
var path = require("path")
var glob = require("glob")
-var slide = require("slide")
-var asyncMap = slide.asyncMap
-var semver = require("semver")
+var normalizeData = require("normalize-package-data")
// put more stuff on here to customize.
readJson.extraSet = [
]
var typoWarned = {}
-// http://registry.npmjs.org/-/fields
-var typos = { "dependancies": "dependencies"
- , "dependecies": "dependencies"
- , "depdenencies": "dependencies"
- , "devEependencies": "devDependencies"
- , "depends": "dependencies"
- , "dev-dependencies": "devDependencies"
- , "devDependences": "devDependencies"
- , "devDepenencies": "devDependencies"
- , "devdependencies": "devDependencies"
- , "repostitory": "repository"
- , "prefereGlobal": "preferGlobal"
- , "hompage": "homepage"
- , "hampage": "homepage"
- , "autohr": "author"
- , "autor": "author"
- , "contributers": "contributors"
- , "publicationConfig": "publishConfig"
- }
-var bugsTypos = { "web": "url", "name": "url" }
-var scriptTypos = { "server": "start", "tests": "test" }
-var depTypes = [ "dependencies"
- , "devDependencies"
- , "optionalDependencies" ]
function readJson (file, cb) {
readJson.extras = extras
function extras (file, data, cb) {
- asyncMap(readJson.extraSet, function (fn, cb) {
- return fn(file, data, cb)
- }, function (er) {
- if (er) return cb(er);
- final(file, data, cb)
+ var set = readJson.extraSet
+ var n = set.length
+ var errState = null
+ set.forEach(function (fn) {
+ fn(file, data, then)
})
+ function then(er) {
+ if (errState) return;
+ if (er) return cb(errState = er);
+ if (--n === 0) final(file, data, cb);
+ }
}
function gypfile (file, data, cb) {
var dir = path.dirname(file)
var s = data.scripts || {}
- if (s.install === "node-gyp rebuild" && !s.preinstall)
- data.gypfile = true
if (s.install || s.preinstall)
return cb(null, data);
glob("*.gyp", { cwd: dir }, function (er, files) {
}
function final (file, data, cb) {
- var ret = validName(file, data)
- if (ret !== true) return cb(ret);
- ret = validVersion(file, data)
- if (ret !== true) return cb(ret);
-
- data._id = data.name + "@" + data.version
- typoWarn(file, data)
- validRepo(file, data)
- validFiles(file, data)
- validBin(file, data)
- validMan(file, data)
- validBundled(file, data)
- objectifyDeps(file, data)
- unParsePeople(file, data)
- parsePeople(file, data)
-
- if (data.description &&
- typeof data.description !== 'string') {
- warn(file, data,
- "'description' field should be a string")
- delete data.description
+ var pId = makePackageId(data)
+ function warn(msg) {
+ if (typoWarned[pId]) return;
+ readJson.log.warn("package.json", pId, msg)
}
-
- if (data.readme && !data.description)
- readmeDescription(file, data)
-
+ try {
+ normalizeData(data, warn)
+ }
+ catch (error) {
+ return cb(error)
+ }
+ typoWarned[pId] = true
readJson.cache.set(file, data)
cb(null, data)
}
+function makePackageId (data) {
+ return cleanString(data.name) + "@" + cleanString(data.version)
+}
+
+function cleanString(str) {
+ return (!str || typeof(str) !== "string") ? "" : str.trim()
+}
// /**package { "name": "foo", "version": "1.2.3", ... } **/
function parseIndex (data) {
e.file = file
return e
}
-
-// a warning for deprecated or likely-incorrect fields
-function typoWarn (file, data) {
- if (data.modules) {
- warn(file, data,
- "'modules' is deprecated")
- delete data.modules
- }
- Object.keys(typos).forEach(function (d) {
- checkTypo(file, data, d)
- })
- bugsTypoWarn(file, data)
- scriptTypoWarn(file, data)
- noreadmeWarn(file, data)
- typoWarned[data._id] = true
-}
-
-function noreadmeWarn (file, data) {
- if (data.readme) return;
- warn(file, data, "No README.md file found!")
- data.readme = "ERROR: No README.md file found!"
-}
-
-function checkTypo (file, data, d) {
- if (!data.hasOwnProperty(d)) return;
- warn(file, data,
- "'" + d + "' should probably be '" + typos[d] + "'" )
-}
-
-function bugsTypoWarn (file, data) {
- var b = data.bugs
- if (!b || typeof b !== "object") return
- Object.keys(b).forEach(function (k) {
- if (bugsTypos[k]) {
- b[bugsTypos[k]] = b[k]
- delete b[k]
- }
- })
-}
-
-function scriptTypoWarn (file, data) {
- var s = data.scripts
- if (!s || typeof s !== "object") return
- Object.keys(s).forEach(function (k) {
- if (scriptTypos[k]) {
- scriptWarn_(file, data, k)
- }
- })
-}
-function scriptWarn_ (file, data, k) {
- warn(file, data, "scripts['" + k + "'] should probably " +
- "be scripts['" + scriptTypos[k] + "']")
-}
-
-function validRepo (file, data) {
- if (data.repostories) {
- warnRepositories(file, data)
- }
- if (!data.repository) return;
- if (typeof data.repository === "string") {
- data.repository = {
- type: "git",
- url: data.repository
- }
- }
- var r = data.repository.url || ""
- // use the non-private urls
- r = r.replace(/^(https?|git):\/\/[^\@]+\@github.com/,
- '$1://github.com')
- r = r.replace(/^https?:\/\/github.com/,
- 'git://github.com')
- if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) {
- warn(file, data, "Probably broken git " +
- "url: " + r)
- }
-}
-function warnRepostories (file, data) {
- warn(file, data,
- "'repositories' (plural) Not supported.\n" +
- "Please pick one as the 'repository' field");
- data.repository = data.repositories[0]
-}
-
-function validFiles (file, data) {
- var files = data.files
- if (files && !Array.isArray(files)) {
- warn(file, data, "Invalid 'files' member")
- delete data.files
- }
-}
-
-function validBin (file, data) {
- if (!data.bin) return;
- if (typeof data.bin === "string") {
- var b = {}
- b[data.name] = data.bin
- data.bin = b
- }
-}
-
-function validMan (file, data) {
- if (!data.man) return;
- if (typeof data.man === "string") {
- data.man = [ data.man ]
- }
-}
-
-function validBundled (file, data) {
- var bdd = "bundledDependencies"
- var bd = "bundleDependencies"
- if (data[bdd] && !data[bd]) {
- data[bd] = data[bdd]
- delete data[bdd]
- }
-
- if (data[bd] && !Array.isArray(data[bd])) {
- warn(file, data, "bundleDependencies " +
- "must be an array")
- }
-}
-
-function objectifyDeps (file, data) {
- depTypes.forEach(function (d) {
- objectifyDep_(file, data, d)
- })
-
- var o = data.optionalDependencies
- if (!o) return;
- var d = data.dependencies || {}
- Object.keys(o).forEach(function (k) {
- d[k] = o[k]
- })
- data.dependencies = d
-}
-function objectifyDep_ (file, data, type) {
- if (!data[type]) return;
- data[type] = depObjectify(file, data, data[type])
-}
-function depObjectify (file, data, deps) {
- if (!deps) return {}
- if (typeof deps === "string") {
- deps = deps.trim().split(/[\n\r\s\t ,]+/)
- }
- if (!Array.isArray(deps)) return deps
- var o = {}
- deps.forEach(function (d) {
- d = d.trim().split(/(:?[@\s><=])/)
- var dn = d.shift()
- var dv = d.join("")
- dv = dv.trim()
- dv = dv.replace(/^@/, "")
- o[dn] = dv
- })
- return o
-}
-
-
-function warn (f, d, m) {
- if (typoWarned[d._id]) return;
- readJson.log.warn("package.json", d._id, m)
-}
-
-
-function validName (file, data) {
- if (!data.name) {
- data.name = ""
- return true
- }
- data.name = data.name.trim()
- if (data.name.charAt(0) === "." ||
- data.name.match(/[\/@\s\+%:]/) ||
- data.name !== encodeURIComponent(data.name) ||
- data.name.toLowerCase() === "node_modules" ||
- data.name.toLowerCase() === "favicon.ico") {
- var m = "Invalid name: "
- m += JSON.stringify(data.name)
- return new Error(m)
- }
- return true
-}
-
-
-function parseKeywords (file, data) {
- var kw = data.keywords
- if (typeof kw === "string") {
- kw = kw.split(/,\s+/)
- data.keywords = kw
- }
-}
-
-function validVersion (file, data) {
- var v = data.version
- if (!v) {
- data.version = ""
- return true
- }
- if (!semver.valid(v)) {
- return new Error("invalid version: "+v)
- }
- data.version = semver.clean(data.version)
- return true
-}
-function unParsePeople (file, data) {
- return parsePeople(file, data, true)
-}
-
-function parsePeople (file, data, un) {
- var fn = un ? unParsePerson : parsePerson
- if (data.author) data.author = fn(data.author)
- ;["maintainers", "contributors"].forEach(function (set) {
- if (!Array.isArray(data[set])) return;
- data[set] = data[set].map(fn)
- })
- return data
-}
-
-function unParsePerson (person) {
- if (typeof person === "string") return person
- var name = person.name || ""
- var u = person.url || person.web
- var url = u ? (" ("+u+")") : ""
- var e = person.email || person.mail
- var email = e ? (" <"+e+">") : ""
- return name+email+url
-}
-
-function parsePerson (person) {
- if (typeof person !== "string") return person
- var name = person.match(/^([^\(<]+)/)
- var url = person.match(/\(([^\)]+)\)/)
- var email = person.match(/<([^>]+)>/)
- var obj = {}
- if (name && name[0].trim()) obj.name = name[0].trim()
- if (email) obj.email = email[1];
- if (url) obj.url = url[1];
- return obj
-}
repository:
{ type: 'git',
url: 'git://github.com/isaacs/read-package-json.git' },
+ bugs: {url: "https://github.com/isaacs/read-package-json/issues" },
main: 'read-json.js',
scripts: { test: 'tap test/*.js' },
dependencies:
devDependencies: { tap: '~0.2.5' },
optionalDependencies: { npmlog: '0', 'graceful-fs': '~1.1.8' },
_id: 'read-package-json@0.1.1',
- readme: 'ERROR: No README.md file found!' }
+ readme: 'ERROR: No README data found!' }
tap.test('from css', function (t) {
var c = path.join(__dirname, 'fixtures', 'not-json.css')
* `strictSSL` - Set to `true` to require that 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.
* `jar` - Set to `false` if you don't want cookies to be remembered for future use or define your custom cookie jar (see examples section)
* `aws` - object containing aws signing information, should have the properties `key` and `secret` as well as `bucket` unless you're specifying your bucket as part of the path, or you are making a request that 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.
+* `localAddress` - Local interface to bind for network connections.
The callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body String or Buffer.
, qs = require('qs')
, querystring = require('querystring')
, crypto = require('crypto')
-
+
, oauth = require('oauth-sign')
, hawk = require('hawk')
, aws = require('aws-sign')
+ , httpSignature = require('http-signature')
, uuid = require('node-uuid')
, mime = require('mime')
, tunnel = require('tunnel-agent')
, ForeverAgent = require('forever-agent')
, FormData = require('form-data')
-
+
, Cookie = require('cookie-jar')
, CookieJar = Cookie.Jar
, cookieJar = new CookieJar
if (typeof options === 'string') {
options = {uri:options}
}
-
+
var reserved = Object.keys(Request.prototype)
for (var i in options) {
if (reserved.indexOf(i) === -1) {
}
}
}
-
+
+ if (options.method) {
+ this.explicitMethod = true
+ }
+
this.init(options)
}
util.inherits(Request, stream.Stream)
// this function is called from both the constructor and on redirect.
var self = this
if (!options) options = {}
-
- self.method = options.method || 'GET'
-
+
+ if (!self.method) self.method = options.method || 'GET'
+ self.localAddress = options.localAddress
+
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
} else {
if (typeof self.uri == "string") self.uri = url.parse(self.uri)
}
-
+
+ if (self.strictSSL === false) {
+ self.rejectUnauthorized = false
+ }
+
if (self.proxy) {
if (typeof self.proxy == 'string') self.proxy = url.parse(self.proxy)
var tunnelOptions = { proxy: { host: self.proxy.hostname
, port: +self.proxy.port
, proxyAuth: self.proxy.auth
- , headers: { Host: self.uri.hostname + ':' +
+ , headers: { Host: self.uri.hostname + ':' +
(self.uri.port || self.uri.protocol === 'https:' ? 443 : 80) }}
+ , rejectUnauthorized: self.rejectUnauthorized
, ca: this.ca }
self.agent = tunnelFn(tunnelOptions)
}
self.setHost = true
}
-
+
self.jar(self._jar || options.jar)
if (!self.uri.pathname) {self.uri.pathname = '/'}
self.clientErrorHandler = function (error) {
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) }
if (options.form) {
self.form(options.form)
}
-
+
if (options.qs) self.qs(options.qs)
if (self.uri.path) {
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) {
self.auth(
- options.auth.user || options.auth.username,
+ (options.auth.user==="") ? options.auth.user : (options.auth.user || options.auth.username ),
options.auth.pass || options.auth.password,
options.auth.sendImmediately)
}
if (self.uri.auth && !self.headers.authorization) {
var authPieces = self.uri.auth.split(':').map(function(item){ return querystring.unescape(item) })
- self.auth(authPieces[0], authPieces[1], true)
+ self.auth(authPieces[0], authPieces.slice(1).join(':'), true)
}
if (self.proxy && self.proxy.auth && !self.headers['proxy-authorization'] && !self.tunnel) {
self.headers['proxy-authorization'] = "Basic " + toBase64(self.proxy.auth.split(':').map(function(item){ return querystring.unescape(item)}).join(':'))
}
-
+
if (self.proxy && !self.tunnel) self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
if (options.json) {
self.agentClass = self.httpModule.Agent
}
}
-
- if (self.strictSSL === false) {
- self.rejectUnauthorized = false
- }
if (self.pool === false) {
self.agent = false
}
if (self._json && !self.headers['content-type'] && !self.headers['Content-Type'])
self.headers['content-type'] = 'application/json'
- if (src.method && !self.method) {
+ if (src.method && !self.explicitMethod) {
self.method = src.method
}
}
process.nextTick(function () {
if (self._aborted) return
-
+
if (self._form) {
self.setHeaders(self._form.getHeaders())
self._form.pipe(self)
var tunnelOptions = { proxy: { host: self.proxy.hostname
, port: +self.proxy.port
, proxyAuth: self.proxy.auth }
+ , rejectUnauthorized: self.rejectUnauthorized
, ca: self.ca }
self.agent = tunnelFn(tunnelOptions)
return
e.code = "ETIMEDOUT"
self.emit("error", e)
}, self.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.on('error', self.clientErrorHandler)
self.req.on('drain', function() {
self.emit('drain')
response.on('end', function() {
debug('response end', self.uri.href, response.statusCode, response.headers)
});
-
+
if (response.connection.listeners('error').indexOf(self._parserErrorHandler) === -1) {
response.connection.once('error', self._parserErrorHandler)
}
delete self.agent
delete self._started
if (response.statusCode != 401) {
+ // Remove parameters from the previous response, unless this is the second request
+ // for a server that requires digest authentication.
delete self.body
delete self._form
+ if (self.headers) {
+ delete self.headers.host
+ delete self.headers['content-type']
+ delete self.headers['content-length']
+ }
}
- if (self.headers) {
- delete self.headers.host
- delete self.headers['content-type']
- delete self.headers['content-length']
- }
+
+ self.emit('redirect');
+
self.init()
return // Ignore the rest of the response
} else {
}
}
+ self.emit('response', response)
+
self.dests.forEach(function (dest) {
self.pipeDest(dest)
})
})
response.on("close", function () {self.emit("close")})
- self.emit('response', response)
-
if (self.callback) {
var buffer = []
var bodyLen = 0
} catch (e) {}
}
debug('emitting complete', self.uri.href)
+ if(response.body == undefined && !self._json) {
+ response.body = "";
+ }
self.emit('complete', response, response.body)
})
}
Request.prototype.abort = function () {
this._aborted = true
-
+
if (this.req) {
this.req.abort()
}
else if (this.response) {
this.response.abort()
}
-
+
this.emit("abort")
}
var base
if (!clobber && this.uri.query) base = qs.parse(this.uri.query)
else base = {}
-
+
for (var i in q) {
base[i] = q[i]
}
if (qs.stringify(base) === ''){
return this
}
-
+
this.uri = url.parse(this.uri.href.split('?')[0] + '?' + qs.stringify(base))
this.url = this.uri
-
+ this.path = this.uri.path
+
return this
}
Request.prototype.form = function (form) {
this.headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
this.body = qs.stringify(form).toString('utf8')
return this
- }
+ }
// create form-data object
this._form = new FormData()
return this._form
if (self.preambleCRLF) {
self.body.push(new Buffer('\r\n'))
}
-
+
multipart.forEach(function (part) {
var body = part.body
if(body == null) throw Error('Body attribute missing in multipart.')
return result
}
Request.prototype.auth = function (user, pass, sendImmediately) {
- if (typeof user !== 'string' || typeof pass !== 'string') {
+ if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) {
throw new Error('auth() received invalid user or password')
}
this._user = user
}
auth.resource = aws.canonicalizeResource(auth.resource)
this.setHeader('authorization', aws.authorization(auth))
-
+
+ return this
+}
+Request.prototype.httpSignature = function (opts) {
+ var req = this
+ httpSignature.signRequest({
+ getHeader: function(header) {
+ return getHeader(header, req.headers)
+ },
+ setHeader: function(header, value) {
+ req.setHeader(header, value)
+ },
+ method: this.method,
+ path: this.path
+ }, opts)
+ debug('httpSignature authorization', getHeader('authorization', this.headers))
+
return this
}
Request.prototype.oauth = function (_oauth) {
var form
- if (this.headers['content-type'] &&
+ if (this.headers['content-type'] &&
this.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
- 'application/x-www-form-urlencoded'
+ 'application/x-www-form-urlencoded'
) {
form = qs.parse(this.body)
}
if (this.uri.query) {
form = qs.parse(this.uri.query)
- }
+ }
if (!form) form = {}
var oa = {}
for (var i in form) oa[i] = form[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, '')
-
+
oa.oauth_signature_method = 'HMAC-SHA1'
-
+
var consumer_secret = oa.oauth_consumer_secret
delete oa.oauth_consumer_secret
var token_secret = oa.oauth_token_secret
var baseurl = this.uri.protocol + '//' + this.uri.host + this.uri.pathname
var signature = oauth.hmacsign(this.method, baseurl, oa, consumer_secret, token_secret)
-
+
// oa.oauth_signature = signature
for (var i in form) {
if ( i.slice(0, 'oauth_') in _oauth) {
- // skip
+ // skip
} else {
delete oa['oauth_'+i]
if (i !== 'x_auth_mode') delete oa[i]
}
Request.prototype.jar = function (jar) {
var cookies
-
+
if (this._redirectsFollowed === 0) {
this.originalCookieHeader = this.headers.cookie
}
-
+
if (jar === false) {
// disable cookies
cookies = false
// fetch cookie from the global cookie jar
cookies = cookieJar.get({ url: this.uri.href })
}
-
+
if (cookies && cookies.length) {
var cookieString = cookies.map(function (c) {
return c.name + "=" + c.value
request.head = function (uri, options, callback) {
var params = initParams(uri, options, callback)
params.options.method = 'HEAD'
- if (params.options.body ||
- params.options.requestBodyStream ||
- (params.options.json && typeof params.options.json !== 'boolean') ||
+ if (params.options.body ||
+ params.options.requestBodyStream ||
+ (params.options.json && typeof params.options.json !== 'boolean') ||
params.options.multipart) {
throw new Error("HTTP HEAD requests MUST NOT include a request body.")
}
// Safe toJSON
-function getSafe (self, uuid) {
+function getSafe (self, uuid) {
if (typeof self === 'object' || typeof self === 'function') var safe = {}
if (Array.isArray(self)) var safe = []
var recurse = []
-
+
Object.defineProperty(self, uuid, {})
-
+
var attrs = Object.keys(self).filter(function (i) {
- if (i === uuid) return false
+ if (i === uuid) return false
if ( (typeof self[i] !== 'object' && typeof self[i] !== 'function') || self[i] === null) return true
return !(Object.getOwnPropertyDescriptor(self[i], uuid))
})
-
-
+
+
for (var i=0;i<attrs.length;i++) {
- if ( (typeof self[attrs[i]] !== 'object' && typeof self[attrs[i]] !== 'function') ||
+ if ( (typeof self[attrs[i]] !== 'object' && typeof self[attrs[i]] !== 'function') ||
self[attrs[i]] === null
) {
safe[attrs[i]] = self[attrs[i]]
for (var i=0;i<recurse.length;i++) {
safe[recurse[i]] = getSafe(self[recurse[i]], uuid)
}
-
+
return safe
}
--- /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.
},
"name": "aws-sign",
"description": "AWS signing. Originally pulled from LearnBoost/knox, maintained as vendor in request, now a standalone module.",
- "version": "0.2.0",
+ "version": "0.3.0",
"repository": {
"url": "https://github.com/mikeal/aws-sign"
},
"engines": {
"node": "*"
},
- "_id": "aws-sign@0.2.0",
- "readme": "ERROR: No README.md file found!",
- "dist": {
- "shasum": "46d6af187cc38dffdb83670acd860bcd220c74eb"
+ "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"
},
- "_from": "aws-sign@~0.2.0",
- "_resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.2.0.tgz"
+ "_id": "aws-sign@0.3.0",
+ "_from": "aws-sign@~0.3.0"
}
--- /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
+cookie-jar
+==========
+
+Cookie Jar. Originally pulled from LearnBoost/tobi, maintained as vendor in request, now a standalone module.
},
"name": "cookie-jar",
"description": "Cookie Jar. Originally pulled form tobi, maintained as vendor in request, now a standalone module.",
- "version": "0.2.0",
+ "version": "0.3.0",
"repository": {
"url": "https://github.com/mikeal/cookie-jar"
},
"engines": {
"node": "*"
},
- "_id": "cookie-jar@0.2.0",
- "readme": "ERROR: No README.md file found!",
- "dist": {
- "shasum": "081cfd8f8ff4b8a93c7fc12e8d0fc5218e1e45a3"
+ "readme": "cookie-jar\n==========\n\nCookie Jar. Originally pulled from LearnBoost/tobi, maintained as vendor in request, now a standalone module.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/mikeal/cookie-jar/issues"
},
- "_from": "cookie-jar@~0.2.0",
- "_resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.2.0.tgz"
+ "_id": "cookie-jar@0.3.0",
+ "_from": "cookie-jar@~0.3.0"
}
--- /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.
ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest
function createConnectionSSL (port, host, options) {
- options.port = port
- options.host = host
- return tls.connect(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);
}
},
"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.2.0",
+ "version": "0.5.0",
"repository": {
"url": "https://github.com/mikeal/forever-agent"
},
"engines": {
"node": "*"
},
- "_id": "forever-agent@0.2.0",
- "readme": "ERROR: No README.md file found!",
- "dist": {
- "shasum": "5dc7762818ad15c3cac9d9f99aceedbee15734be"
+ "readme": "forever-agent\n=============\n\nHTTP Agent that keeps socket connections alive between keep-alive requests. Formerly part of mikeal/request, now a standalone module.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/mikeal/forever-agent/issues"
},
- "_from": "forever-agent@~0.2.0",
- "_resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.2.0.tgz"
+ "_id": "forever-agent@0.5.0",
+ "_from": "forever-agent@~0.5.0"
}
language: node_js
node_js:
- - 0.6
- - 0.4
\ No newline at end of file
+ - "0.6"
+ - "0.8"
-# form-data
+# Form-Data [![Build Status](https://travis-ci.org/alexindigo/node-form-data.png?branch=master)](https://travis-ci.org/alexindigo/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.
## Install
-Sorry, this isn't ready for you yet.
+```
+npm install form-data
+```
## Usage
});
```
-For edge cases, like POST request to URL with query string or to pass HTTP auth creadentials, object can be passed to `form.submit()` as first parameter:
+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({
});
```
+## TODO
-[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface
+- 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.
'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 (value.path) {
+ if (options.filename || value.path) {
header +=
- '; filename="' + path.basename(value.path) + '"' + FormData.LINE_BREAK +
- 'Content-Type: ' + mime.lookup(value.path);
+ '; 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')) {
--- /dev/null
+{
+ "folders":
+ [
+ {
+ "path": "/Users/alexi/Dropbox/Projects/node-form-data"
+ }
+ ]
+}
--- /dev/null
+{
+ "auto_complete":
+ {
+ "selected_items":
+ [
+ [
+ "back",
+ "background-clip"
+ ],
+ [
+ "di",
+ "display"
+ ],
+ [
+ "background-",
+ "background-position"
+ ],
+ [
+ "de",
+ "defaults"
+ ],
+ [
+ "no",
+ "normal"
+ ],
+ [
+ "line",
+ "line-height"
+ ]
+ ]
+ },
+ "buffers":
+ [
+ {
+ "file": "test/integration/test-pipe.js",
+ "settings":
+ {
+ "buffer_size": 3625,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "file": "package.json",
+ "settings":
+ {
+ "buffer_size": 673,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "file": "lib/form_data.js",
+ "settings":
+ {
+ "buffer_size": 7510,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "contents": "/*!\n * jQuery JavaScript Library v1.8.3\n * http://jquery.com/\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n *\n * Copyright 2012 jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)\n */\n(function( window, undefined ) {\nvar\n // A central reference to the root jQuery(document)\n rootjQuery,\n\n // The deferred used on DOM ready\n readyList,\n\n // Use the correct document accordingly with window argument (sandbox)\n document = window.document,\n location = window.location,\n navigator = window.navigator,\n\n // Map over jQuery in case of overwrite\n _jQuery = window.jQuery,\n\n // Map over the $ in case of overwrite\n _$ = window.$,\n\n // Save a reference to some core methods\n core_push = Array.prototype.push,\n core_slice = Array.prototype.slice,\n core_indexOf = Array.prototype.indexOf,\n core_toString = Object.prototype.toString,\n core_hasOwn = Object.prototype.hasOwnProperty,\n core_trim = String.prototype.trim,\n\n // Define a local copy of jQuery\n jQuery = function( selector, context ) {\n // The jQuery object is actually just the init constructor 'enhanced'\n return new jQuery.fn.init( selector, context, rootjQuery );\n },\n\n // Used for matching numbers\n core_pnum = /[\\-+]?(?:\\d*\\.|)\\d+(?:[eE][\\-+]?\\d+|)/.source,\n\n // Used for detecting and trimming whitespace\n core_rnotwhite = /\\S/,\n core_rspace = /\\s+/,\n\n // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)\n rtrim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,\n\n // A simple way to check for HTML strings\n // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)\n rquickExpr = /^(?:[^#<]*(<[\\w\\W]+>)[^>]*$|#([\\w\\-]*)$)/,\n\n // Match a standalone tag\n rsingleTag = /^<(\\w+)\\s*\\/?>(?:<\\/\\1>|)$/,\n\n // JSON RegExp\n rvalidchars = /^[\\],:{}\\s]*$/,\n rvalidbraces = /(?:^|:|,)(?:\\s*\\[)+/g,\n rvalidescape = /\\\\(?:[\"\\\\\\/bfnrt]|u[\\da-fA-F]{4})/g,\n rvalidtokens = /\"[^\"\\\\\\r\\n]*\"|true|false|null|-?(?:\\d\\d*\\.|)\\d+(?:[eE][\\-+]?\\d+|)/g,\n\n // Matches dashed string for camelizing\n rmsPrefix = /^-ms-/,\n rdashAlpha = /-([\\da-z])/gi,\n\n // Used by jQuery.camelCase as callback to replace()\n fcamelCase = function( all, letter ) {\n return ( letter + \"\" ).toUpperCase();\n },\n\n // The ready event handler and self cleanup method\n DOMContentLoaded = function() {\n if ( document.addEventListener ) {\n document.removeEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n jQuery.ready();\n } else if ( document.readyState === \"complete\" ) {\n // we're here because readyState === \"complete\" in oldIE\n // which is good enough for us to call the dom ready!\n document.detachEvent( \"onreadystatechange\", DOMContentLoaded );\n jQuery.ready();\n }\n },\n\n // [[Class]] -> type pairs\n class2type = {};\n\njQuery.fn = jQuery.prototype = {\n constructor: jQuery,\n init: function( selector, context, rootjQuery ) {\n var match, elem, ret, doc;\n\n // Handle $(\"\"), $(null), $(undefined), $(false)\n if ( !selector ) {\n return this;\n }\n\n // Handle $(DOMElement)\n if ( selector.nodeType ) {\n this.context = this[0] = selector;\n this.length = 1;\n return this;\n }\n\n // Handle HTML strings\n if ( typeof selector === \"string\" ) {\n if ( selector.charAt(0) === \"<\" && selector.charAt( selector.length - 1 ) === \">\" && selector.length >= 3 ) {\n // Assume that strings that start and end with <> are HTML and skip the regex check\n match = [ null, selector, null ];\n\n } else {\n match = rquickExpr.exec( selector );\n }\n\n // Match html or make sure no context is specified for #id\n if ( match && (match[1] || !context) ) {\n\n // HANDLE: $(html) -> $(array)\n if ( match[1] ) {\n context = context instanceof jQuery ? context[0] : context;\n doc = ( context && context.nodeType ? context.ownerDocument || context : document );\n\n // scripts is true for back-compat\n selector = jQuery.parseHTML( match[1], doc, true );\n if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {\n this.attr.call( selector, context, true );\n }\n\n return jQuery.merge( this, selector );\n\n // HANDLE: $(#id)\n } else {\n elem = document.getElementById( match[2] );\n\n // Check parentNode to catch when Blackberry 4.6 returns\n // nodes that are no longer in the document #6963\n if ( elem && elem.parentNode ) {\n // Handle the case where IE and Opera return items\n // by name instead of ID\n if ( elem.id !== match[2] ) {\n return rootjQuery.find( selector );\n }\n\n // Otherwise, we inject the element directly into the jQuery object\n this.length = 1;\n this[0] = elem;\n }\n\n this.context = document;\n this.selector = selector;\n return this;\n }\n\n // HANDLE: $(expr, $(...))\n } else if ( !context || context.jquery ) {\n return ( context || rootjQuery ).find( selector );\n\n // HANDLE: $(expr, context)\n // (which is just equivalent to: $(context).find(expr)\n } else {\n return this.constructor( context ).find( selector );\n }\n\n // HANDLE: $(function)\n // Shortcut for document ready\n } else if ( jQuery.isFunction( selector ) ) {\n return rootjQuery.ready( selector );\n }\n\n if ( selector.selector !== undefined ) {\n this.selector = selector.selector;\n this.context = selector.context;\n }\n\n return jQuery.makeArray( selector, this );\n },\n\n // Start with an empty selector\n selector: \"\",\n\n // The current version of jQuery being used\n jquery: \"1.8.3\",\n\n // The default length of a jQuery object is 0\n length: 0,\n\n // The number of elements contained in the matched element set\n size: function() {\n return this.length;\n },\n\n toArray: function() {\n return core_slice.call( this );\n },\n\n // Get the Nth element in the matched element set OR\n // Get the whole matched element set as a clean array\n get: function( num ) {\n return num == null ?\n\n // Return a 'clean' array\n this.toArray() :\n\n // Return just the object\n ( num < 0 ? this[ this.length + num ] : this[ num ] );\n },\n\n // Take an array of elements and push it onto the stack\n // (returning the new matched element set)\n pushStack: function( elems, name, selector ) {\n\n // Build a new jQuery matched element set\n var ret = jQuery.merge( this.constructor(), elems );\n\n // Add the old object onto the stack (as a reference)\n ret.prevObject = this;\n\n ret.context = this.context;\n\n if ( name === \"find\" ) {\n ret.selector = this.selector + ( this.selector ? \" \" : \"\" ) + selector;\n } else if ( name ) {\n ret.selector = this.selector + \".\" + name + \"(\" + selector + \")\";\n }\n\n // Return the newly-formed element set\n return ret;\n },\n\n // Execute a callback for every element in the matched set.\n // (You can seed the arguments with an array of args, but this is\n // only used internally.)\n each: function( callback, args ) {\n return jQuery.each( this, callback, args );\n },\n\n ready: function( fn ) {\n // Add the callback\n jQuery.ready.promise().done( fn );\n\n return this;\n },\n\n eq: function( i ) {\n i = +i;\n return i === -1 ?\n this.slice( i ) :\n this.slice( i, i + 1 );\n },\n\n first: function() {\n return this.eq( 0 );\n },\n\n last: function() {\n return this.eq( -1 );\n },\n\n slice: function() {\n return this.pushStack( core_slice.apply( this, arguments ),\n \"slice\", core_slice.call(arguments).join(\",\") );\n },\n\n map: function( callback ) {\n return this.pushStack( jQuery.map(this, function( elem, i ) {\n return callback.call( elem, i, elem );\n }));\n },\n\n end: function() {\n return this.prevObject || this.constructor(null);\n },\n\n // For internal use only.\n // Behaves like an Array's method, not like a jQuery method.\n push: core_push,\n sort: [].sort,\n splice: [].splice\n};\n\n// Give the init function the jQuery prototype for later instantiation\njQuery.fn.init.prototype = jQuery.fn;\n\njQuery.extend = jQuery.fn.extend = function() {\n var options, name, src, copy, copyIsArray, clone,\n target = arguments[0] || {},\n i = 1,\n length = arguments.length,\n deep = false;\n\n // Handle a deep copy situation\n if ( typeof target === \"boolean\" ) {\n deep = target;\n target = arguments[1] || {};\n // skip the boolean and the target\n i = 2;\n }\n\n // Handle case when target is a string or something (possible in deep copy)\n if ( typeof target !== \"object\" && !jQuery.isFunction(target) ) {\n target = {};\n }\n\n // extend jQuery itself if only one argument is passed\n if ( length === i ) {\n target = this;\n --i;\n }\n\n for ( ; i < length; i++ ) {\n // Only deal with non-null/undefined values\n if ( (options = arguments[ i ]) != null ) {\n // Extend the base object\n for ( name in options ) {\n src = target[ name ];\n copy = options[ name ];\n\n // Prevent never-ending loop\n if ( target === copy ) {\n continue;\n }\n\n // Recurse if we're merging plain objects or arrays\n if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {\n if ( copyIsArray ) {\n copyIsArray = false;\n clone = src && jQuery.isArray(src) ? src : [];\n\n } else {\n clone = src && jQuery.isPlainObject(src) ? src : {};\n }\n\n // Never move original objects, clone them\n target[ name ] = jQuery.extend( deep, clone, copy );\n\n // Don't bring in undefined values\n } else if ( copy !== undefined ) {\n target[ name ] = copy;\n }\n }\n }\n }\n\n // Return the modified object\n return target;\n};\n\njQuery.extend({\n noConflict: function( deep ) {\n if ( window.$ === jQuery ) {\n window.$ = _$;\n }\n\n if ( deep && window.jQuery === jQuery ) {\n window.jQuery = _jQuery;\n }\n\n return jQuery;\n },\n\n // Is the DOM ready to be used? Set to true once it occurs.\n isReady: false,\n\n // A counter to track how many items to wait for before\n // the ready event fires. See #6781\n readyWait: 1,\n\n // Hold (or release) the ready event\n holdReady: function( hold ) {\n if ( hold ) {\n jQuery.readyWait++;\n } else {\n jQuery.ready( true );\n }\n },\n\n // Handle when the DOM is ready\n ready: function( wait ) {\n\n // Abort if there are pending holds or we're already ready\n if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n return;\n }\n\n // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n if ( !document.body ) {\n return setTimeout( jQuery.ready, 1 );\n }\n\n // Remember that the DOM is ready\n jQuery.isReady = true;\n\n // If a normal DOM Ready event fired, decrement, and wait if need be\n if ( wait !== true && --jQuery.readyWait > 0 ) {\n return;\n }\n\n // If there are functions bound, to execute\n readyList.resolveWith( document, [ jQuery ] );\n\n // Trigger any bound ready events\n if ( jQuery.fn.trigger ) {\n jQuery( document ).trigger(\"ready\").off(\"ready\");\n }\n },\n\n // See test/unit/core.js for details concerning isFunction.\n // Since version 1.3, DOM methods and functions like alert\n // aren't supported. They return false on IE (#2968).\n isFunction: function( obj ) {\n return jQuery.type(obj) === \"function\";\n },\n\n isArray: Array.isArray || function( obj ) {\n return jQuery.type(obj) === \"array\";\n },\n\n isWindow: function( obj ) {\n return obj != null && obj == obj.window;\n },\n\n isNumeric: function( obj ) {\n return !isNaN( parseFloat(obj) ) && isFinite( obj );\n },\n\n type: function( obj ) {\n return obj == null ?\n String( obj ) :\n class2type[ core_toString.call(obj) ] || \"object\";\n },\n\n isPlainObject: function( obj ) {\n // Must be an Object.\n // Because of IE, we also have to check the presence of the constructor property.\n // Make sure that DOM nodes and window objects don't pass through, as well\n if ( !obj || jQuery.type(obj) !== \"object\" || obj.nodeType || jQuery.isWindow( obj ) ) {\n return false;\n }\n\n try {\n // Not own constructor property must be Object\n if ( obj.constructor &&\n !core_hasOwn.call(obj, \"constructor\") &&\n !core_hasOwn.call(obj.constructor.prototype, \"isPrototypeOf\") ) {\n return false;\n }\n } catch ( e ) {\n // IE8,9 Will throw exceptions on certain host objects #9897\n return false;\n }\n\n // Own properties are enumerated firstly, so to speed up,\n // if last one is own, then all properties are own.\n\n var key;\n for ( key in obj ) {}\n\n return key === undefined || core_hasOwn.call( obj, key );\n },\n\n isEmptyObject: function( obj ) {\n var name;\n for ( name in obj ) {\n return false;\n }\n return true;\n },\n\n error: function( msg ) {\n throw new Error( msg );\n },\n\n // data: string of html\n // context (optional): If specified, the fragment will be created in this context, defaults to document\n // scripts (optional): If true, will include scripts passed in the html string\n parseHTML: function( data, context, scripts ) {\n var parsed;\n if ( !data || typeof data !== \"string\" ) {\n return null;\n }\n if ( typeof context === \"boolean\" ) {\n scripts = context;\n context = 0;\n }\n context = context || document;\n\n // Single tag\n if ( (parsed = rsingleTag.exec( data )) ) {\n return [ context.createElement( parsed[1] ) ];\n }\n\n parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );\n return jQuery.merge( [],\n (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );\n },\n\n parseJSON: function( data ) {\n if ( !data || typeof data !== \"string\") {\n return null;\n }\n\n // Make sure leading/trailing whitespace is removed (IE can't handle it)\n data = jQuery.trim( data );\n\n // Attempt to parse using the native JSON parser first\n if ( window.JSON && window.JSON.parse ) {\n return window.JSON.parse( data );\n }\n\n // Make sure the incoming data is actual JSON\n // Logic borrowed from http://json.org/json2.js\n if ( rvalidchars.test( data.replace( rvalidescape, \"@\" )\n .replace( rvalidtokens, \"]\" )\n .replace( rvalidbraces, \"\")) ) {\n\n return ( new Function( \"return \" + data ) )();\n\n }\n jQuery.error( \"Invalid JSON: \" + data );\n },\n\n // Cross-browser xml parsing\n parseXML: function( data ) {\n var xml, tmp;\n if ( !data || typeof data !== \"string\" ) {\n return null;\n }\n try {\n if ( window.DOMParser ) { // Standard\n tmp = new DOMParser();\n xml = tmp.parseFromString( data , \"text/xml\" );\n } else { // IE\n xml = new ActiveXObject( \"Microsoft.XMLDOM\" );\n xml.async = \"false\";\n xml.loadXML( data );\n }\n } catch( e ) {\n xml = undefined;\n }\n if ( !xml || !xml.documentElement || xml.getElementsByTagName( \"parsererror\" ).length ) {\n jQuery.error( \"Invalid XML: \" + data );\n }\n return xml;\n },\n\n noop: function() {},\n\n // Evaluates a script in a global context\n // Workarounds based on findings by Jim Driscoll\n // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context\n globalEval: function( data ) {\n if ( data && core_rnotwhite.test( data ) ) {\n // We use execScript on Internet Explorer\n // We use an anonymous function so that context is window\n // rather than jQuery in Firefox\n ( window.execScript || function( data ) {\n window[ \"eval\" ].call( window, data );\n } )( data );\n }\n },\n\n // Convert dashed to camelCase; used by the css and data modules\n // Microsoft forgot to hump their vendor prefix (#9572)\n camelCase: function( string ) {\n return string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n },\n\n nodeName: function( elem, name ) {\n return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n },\n\n // args is for internal usage only\n each: function( obj, callback, args ) {\n var name,\n i = 0,\n length = obj.length,\n isObj = length === undefined || jQuery.isFunction( obj );\n\n if ( args ) {\n if ( isObj ) {\n for ( name in obj ) {\n if ( callback.apply( obj[ name ], args ) === false ) {\n break;\n }\n }\n } else {\n for ( ; i < length; ) {\n if ( callback.apply( obj[ i++ ], args ) === false ) {\n break;\n }\n }\n }\n\n // A special, fast, case for the most common use of each\n } else {\n if ( isObj ) {\n for ( name in obj ) {\n if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {\n break;\n }\n }\n } else {\n for ( ; i < length; ) {\n if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {\n break;\n }\n }\n }\n }\n\n return obj;\n },\n\n // Use native String.trim function wherever possible\n trim: core_trim && !core_trim.call(\"\\uFEFF\\xA0\") ?\n function( text ) {\n return text == null ?\n \"\" :\n core_trim.call( text );\n } :\n\n // Otherwise use our own trimming functionality\n function( text ) {\n return text == null ?\n \"\" :\n ( text + \"\" ).replace( rtrim, \"\" );\n },\n\n // results is for internal usage only\n makeArray: function( arr, results ) {\n var type,\n ret = results || [];\n\n if ( arr != null ) {\n // The window, strings (and functions) also have 'length'\n // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930\n type = jQuery.type( arr );\n\n if ( arr.length == null || type === \"string\" || type === \"function\" || type === \"regexp\" || jQuery.isWindow( arr ) ) {\n core_push.call( ret, arr );\n } else {\n jQuery.merge( ret, arr );\n }\n }\n\n return ret;\n },\n\n inArray: function( elem, arr, i ) {\n var len;\n\n if ( arr ) {\n if ( core_indexOf ) {\n return core_indexOf.call( arr, elem, i );\n }\n\n len = arr.length;\n i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;\n\n for ( ; i < len; i++ ) {\n // Skip accessing in sparse arrays\n if ( i in arr && arr[ i ] === elem ) {\n return i;\n }\n }\n }\n\n return -1;\n },\n\n merge: function( first, second ) {\n var l = second.length,\n i = first.length,\n j = 0;\n\n if ( typeof l === \"number\" ) {\n for ( ; j < l; j++ ) {\n first[ i++ ] = second[ j ];\n }\n\n } else {\n while ( second[j] !== undefined ) {\n first[ i++ ] = second[ j++ ];\n }\n }\n\n first.length = i;\n\n return first;\n },\n\n grep: function( elems, callback, inv ) {\n var retVal,\n ret = [],\n i = 0,\n length = elems.length;\n inv = !!inv;\n\n // Go through the array, only saving the items\n // that pass the validator function\n for ( ; i < length; i++ ) {\n retVal = !!callback( elems[ i ], i );\n if ( inv !== retVal ) {\n ret.push( elems[ i ] );\n }\n }\n\n return ret;\n },\n\n // arg is for internal usage only\n map: function( elems, callback, arg ) {\n var value, key,\n ret = [],\n i = 0,\n length = elems.length,\n // jquery objects are treated as arrays\n isArray = elems instanceof jQuery || length !== undefined && typeof length === \"number\" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;\n\n // Go through the array, translating each of the items to their\n if ( isArray ) {\n for ( ; i < length; i++ ) {\n value = callback( elems[ i ], i, arg );\n\n if ( value != null ) {\n ret[ ret.length ] = value;\n }\n }\n\n // Go through every key on the object,\n } else {\n for ( key in elems ) {\n value = callback( elems[ key ], key, arg );\n\n if ( value != null ) {\n ret[ ret.length ] = value;\n }\n }\n }\n\n // Flatten any nested arrays\n return ret.concat.apply( [], ret );\n },\n\n // A global GUID counter for objects\n guid: 1,\n\n // Bind a function to a context, optionally partially applying any\n // arguments.\n proxy: function( fn, context ) {\n var tmp, args, proxy;\n\n if ( typeof context === \"string\" ) {\n tmp = fn[ context ];\n context = fn;\n fn = tmp;\n }\n\n // Quick check to determine if target is callable, in the spec\n // this throws a TypeError, but we will just return undefined.\n if ( !jQuery.isFunction( fn ) ) {\n return undefined;\n }\n\n // Simulated bind\n args = core_slice.call( arguments, 2 );\n proxy = function() {\n return fn.apply( context, args.concat( core_slice.call( arguments ) ) );\n };\n\n // Set the guid of unique handler to the same of original handler, so it can be removed\n proxy.guid = fn.guid = fn.guid || jQuery.guid++;\n\n return proxy;\n },\n\n // Multifunctional method to get and set values of a collection\n // The value/s can optionally be executed if it's a function\n access: function( elems, fn, key, value, chainable, emptyGet, pass ) {\n var exec,\n bulk = key == null,\n i = 0,\n length = elems.length;\n\n // Sets many values\n if ( key && typeof key === \"object\" ) {\n for ( i in key ) {\n jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );\n }\n chainable = 1;\n\n // Sets one value\n } else if ( value !== undefined ) {\n // Optionally, function values get executed if exec is true\n exec = pass === undefined && jQuery.isFunction( value );\n\n if ( bulk ) {\n // Bulk operations only iterate when executing function values\n if ( exec ) {\n exec = fn;\n fn = function( elem, key, value ) {\n return exec.call( jQuery( elem ), value );\n };\n\n // Otherwise they run against the entire set\n } else {\n fn.call( elems, value );\n fn = null;\n }\n }\n\n if ( fn ) {\n for (; i < length; i++ ) {\n fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );\n }\n }\n\n chainable = 1;\n }\n\n return chainable ?\n elems :\n\n // Gets\n bulk ?\n fn.call( elems ) :\n length ? fn( elems[0], key ) : emptyGet;\n },\n\n now: function() {\n return ( new Date() ).getTime();\n }\n});\n\njQuery.ready.promise = function( obj ) {\n if ( !readyList ) {\n\n readyList = jQuery.Deferred();\n\n // Catch cases where $(document).ready() is called after the browser event has already occurred.\n // we once tried to use readyState \"interactive\" here, but it caused issues like the one\n // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15\n if ( document.readyState === \"complete\" ) {\n // Handle it asynchronously to allow scripts the opportunity to delay ready\n setTimeout( jQuery.ready, 1 );\n\n // Standards-based browsers support DOMContentLoaded\n } else if ( document.addEventListener ) {\n // Use the handy event callback\n document.addEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\n // A fallback to window.onload, that will always work\n window.addEventListener( \"load\", jQuery.ready, false );\n\n // If IE event model is used\n } else {\n // Ensure firing before onload, maybe late but safe also for iframes\n document.attachEvent( \"onreadystatechange\", DOMContentLoaded );\n\n // A fallback to window.onload, that will always work\n window.attachEvent( \"onload\", jQuery.ready );\n\n // If IE and not a frame\n // continually check to see if the document is ready\n var top = false;\n\n try {\n top = window.frameElement == null && document.documentElement;\n } catch(e) {}\n\n if ( top && top.doScroll ) {\n (function doScrollCheck() {\n if ( !jQuery.isReady ) {\n\n try {\n // Use the trick by Diego Perini\n // http://javascript.nwbox.com/IEContentLoaded/\n top.doScroll(\"left\");\n } catch(e) {\n return setTimeout( doScrollCheck, 50 );\n }\n\n // and execute any waiting functions\n jQuery.ready();\n }\n })();\n }\n }\n }\n return readyList.promise( obj );\n};\n\n// Populate the class2type map\njQuery.each(\"Boolean Number String Function Array Date RegExp Object\".split(\" \"), function(i, name) {\n class2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n});\n\n// All jQuery objects should point back to these\nrootjQuery = jQuery(document);\n// String to Object options format cache\nvar optionsCache = {};\n\n// Convert String-formatted options into Object-formatted ones and store in cache\nfunction createOptions( options ) {\n var object = optionsCache[ options ] = {};\n jQuery.each( options.split( core_rspace ), function( _, flag ) {\n object[ flag ] = true;\n });\n return object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n * options: an optional list of space-separated options that will change how\n * the callback list behaves or a more traditional option object\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible options:\n *\n * once: will ensure the callback list can only be fired once (like a Deferred)\n *\n * memory: will keep track of previous values and will call any callback added\n * after the list has been fired right away with the latest \"memorized\"\n * values (like a Deferred)\n *\n * unique: will ensure a callback can only be added once (no duplicate in the list)\n *\n * stopOnFalse: interrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( options ) {\n\n // Convert options from String-formatted to Object-formatted if needed\n // (we check in cache first)\n options = typeof options === \"string\" ?\n ( optionsCache[ options ] || createOptions( options ) ) :\n jQuery.extend( {}, options );\n\n var // Last fire value (for non-forgettable lists)\n memory,\n // Flag to know if list was already fired\n fired,\n // Flag to know if list is currently firing\n firing,\n // First callback to fire (used internally by add and fireWith)\n firingStart,\n // End of the loop when firing\n firingLength,\n // Index of currently firing callback (modified by remove if needed)\n firingIndex,\n // Actual callback list\n list = [],\n // Stack of fire calls for repeatable lists\n stack = !options.once && [],\n // Fire callbacks\n fire = function( data ) {\n memory = options.memory && data;\n fired = true;\n firingIndex = firingStart || 0;\n firingStart = 0;\n firingLength = list.length;\n firing = true;\n for ( ; list && firingIndex < firingLength; firingIndex++ ) {\n if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {\n memory = false; // To prevent further calls using add\n break;\n }\n }\n firing = false;\n if ( list ) {\n if ( stack ) {\n if ( stack.length ) {\n fire( stack.shift() );\n }\n } else if ( memory ) {\n list = [];\n } else {\n self.disable();\n }\n }\n },\n // Actual Callbacks object\n self = {\n // Add a callback or a collection of callbacks to the list\n add: function() {\n if ( list ) {\n // First, we save the current length\n var start = list.length;\n (function add( args ) {\n jQuery.each( args, function( _, arg ) {\n var type = jQuery.type( arg );\n if ( type === \"function\" ) {\n if ( !options.unique || !self.has( arg ) ) {\n list.push( arg );\n }\n } else if ( arg && arg.length && type !== \"string\" ) {\n // Inspect recursively\n add( arg );\n }\n });\n })( arguments );\n // Do we need to add the callbacks to the\n // current firing batch?\n if ( firing ) {\n firingLength = list.length;\n // With memory, if we're not firing then\n // we should call right away\n } else if ( memory ) {\n firingStart = start;\n fire( memory );\n }\n }\n return this;\n },\n // Remove a callback from the list\n remove: function() {\n if ( list ) {\n jQuery.each( arguments, function( _, arg ) {\n var index;\n while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {\n list.splice( index, 1 );\n // Handle firing indexes\n if ( firing ) {\n if ( index <= firingLength ) {\n firingLength--;\n }\n if ( index <= firingIndex ) {\n firingIndex--;\n }\n }\n }\n });\n }\n return this;\n },\n // Control if a given callback is in the list\n has: function( fn ) {\n return jQuery.inArray( fn, list ) > -1;\n },\n // Remove all callbacks from the list\n empty: function() {\n list = [];\n return this;\n },\n // Have the list do nothing anymore\n disable: function() {\n list = stack = memory = undefined;\n return this;\n },\n // Is it disabled?\n disabled: function() {\n return !list;\n },\n // Lock the list in its current state\n lock: function() {\n stack = undefined;\n if ( !memory ) {\n self.disable();\n }\n return this;\n },\n // Is it locked?\n locked: function() {\n return !stack;\n },\n // Call all callbacks with the given context and arguments\n fireWith: function( context, args ) {\n args = args || [];\n args = [ context, args.slice ? args.slice() : args ];\n if ( list && ( !fired || stack ) ) {\n if ( firing ) {\n stack.push( args );\n } else {\n fire( args );\n }\n }\n return this;\n },\n // Call all the callbacks with the given arguments\n fire: function() {\n self.fireWith( this, arguments );\n return this;\n },\n // To know if the callbacks have already been called at least once\n fired: function() {\n return !!fired;\n }\n };\n\n return self;\n};\njQuery.extend({\n\n Deferred: function( func ) {\n var tuples = [\n // action, add listener, listener list, final state\n [ \"resolve\", \"done\", jQuery.Callbacks(\"once memory\"), \"resolved\" ],\n [ \"reject\", \"fail\", jQuery.Callbacks(\"once memory\"), \"rejected\" ],\n [ \"notify\", \"progress\", jQuery.Callbacks(\"memory\") ]\n ],\n state = \"pending\",\n promise = {\n state: function() {\n return state;\n },\n always: function() {\n deferred.done( arguments ).fail( arguments );\n return this;\n },\n then: function( /* fnDone, fnFail, fnProgress */ ) {\n var fns = arguments;\n return jQuery.Deferred(function( newDefer ) {\n jQuery.each( tuples, function( i, tuple ) {\n var action = tuple[ 0 ],\n fn = fns[ i ];\n // deferred[ done | fail | progress ] for forwarding actions to newDefer\n deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?\n function() {\n var returned = fn.apply( this, arguments );\n if ( returned && jQuery.isFunction( returned.promise ) ) {\n returned.promise()\n .done( newDefer.resolve )\n .fail( newDefer.reject )\n .progress( newDefer.notify );\n } else {\n newDefer[ action + \"With\" ]( this === deferred ? newDefer : this, [ returned ] );\n }\n } :\n newDefer[ action ]\n );\n });\n fns = null;\n }).promise();\n },\n // Get a promise for this deferred\n // If obj is provided, the promise aspect is added to the object\n promise: function( obj ) {\n return obj != null ? jQuery.extend( obj, promise ) : promise;\n }\n },\n deferred = {};\n\n // Keep pipe for back-compat\n promise.pipe = promise.then;\n\n // Add list-specific methods\n jQuery.each( tuples, function( i, tuple ) {\n var list = tuple[ 2 ],\n stateString = tuple[ 3 ];\n\n // promise[ done | fail | progress ] = list.add\n promise[ tuple[1] ] = list.add;\n\n // Handle state\n if ( stateString ) {\n list.add(function() {\n // state = [ resolved | rejected ]\n state = stateString;\n\n // [ reject_list | resolve_list ].disable; progress_list.lock\n }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );\n }\n\n // deferred[ resolve | reject | notify ] = list.fire\n deferred[ tuple[0] ] = list.fire;\n deferred[ tuple[0] + \"With\" ] = list.fireWith;\n });\n\n // Make the deferred a promise\n promise.promise( deferred );\n\n // Call given func if any\n if ( func ) {\n func.call( deferred, deferred );\n }\n\n // All done!\n return deferred;\n },\n\n // Deferred helper\n when: function( subordinate /* , ..., subordinateN */ ) {\n var i = 0,\n resolveValues = core_slice.call( arguments ),\n length = resolveValues.length,\n\n // the count of uncompleted subordinates\n remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,\n\n // the master Deferred. If resolveValues consist of only a single Deferred, just use that.\n deferred = remaining === 1 ? subordinate : jQuery.Deferred(),\n\n // Update function for both resolve and progress values\n updateFunc = function( i, contexts, values ) {\n return function( value ) {\n contexts[ i ] = this;\n values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;\n if( values === progressValues ) {\n deferred.notifyWith( contexts, values );\n } else if ( !( --remaining ) ) {\n deferred.resolveWith( contexts, values );\n }\n };\n },\n\n progressValues, progressContexts, resolveContexts;\n\n // add listeners to Deferred subordinates; treat others as resolved\n if ( length > 1 ) {\n progressValues = new Array( length );\n progressContexts = new Array( length );\n resolveContexts = new Array( length );\n for ( ; i < length; i++ ) {\n if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {\n resolveValues[ i ].promise()\n .done( updateFunc( i, resolveContexts, resolveValues ) )\n .fail( deferred.reject )\n .progress( updateFunc( i, progressContexts, progressValues ) );\n } else {\n --remaining;\n }\n }\n }\n\n // if we're not waiting on anything, resolve the master\n if ( !remaining ) {\n deferred.resolveWith( resolveContexts, resolveValues );\n }\n\n return deferred.promise();\n }\n});\njQuery.support = (function() {\n\n var support,\n all,\n a,\n select,\n opt,\n input,\n fragment,\n eventName,\n i,\n isSupported,\n clickFn,\n div = document.createElement(\"div\");\n\n // Setup\n div.setAttribute( \"className\", \"t\" );\n div.innerHTML = \" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>\";\n\n // Support tests won't run in some limited or non-browser environments\n all = div.getElementsByTagName(\"*\");\n a = div.getElementsByTagName(\"a\")[ 0 ];\n if ( !all || !a || !all.length ) {\n return {};\n }\n\n // First batch of tests\n select = document.createElement(\"select\");\n opt = select.appendChild( document.createElement(\"option\") );\n input = div.getElementsByTagName(\"input\")[ 0 ];\n\n a.style.cssText = \"top:1px;float:left;opacity:.5\";\n support = {\n // IE strips leading whitespace when .innerHTML is used\n leadingWhitespace: ( div.firstChild.nodeType === 3 ),\n\n // Make sure that tbody elements aren't automatically inserted\n // IE will insert them into empty tables\n tbody: !div.getElementsByTagName(\"tbody\").length,\n\n // Make sure that link elements get serialized correctly by innerHTML\n // This requires a wrapper element in IE\n htmlSerialize: !!div.getElementsByTagName(\"link\").length,\n\n // Get the style information from getAttribute\n // (IE uses .cssText instead)\n style: /top/.test( a.getAttribute(\"style\") ),\n\n // Make sure that URLs aren't manipulated\n // (IE normalizes it by default)\n hrefNormalized: ( a.getAttribute(\"href\") === \"/a\" ),\n\n // Make sure that element opacity exists\n // (IE uses filter instead)\n // Use a regex to work around a WebKit issue. See #5145\n opacity: /^0.5/.test( a.style.opacity ),\n\n // Verify style float existence\n // (IE uses styleFloat instead of cssFloat)\n cssFloat: !!a.style.cssFloat,\n\n // Make sure that if no value is specified for a checkbox\n // that it defaults to \"on\".\n // (WebKit defaults to \"\" instead)\n checkOn: ( input.value === \"on\" ),\n\n // Make sure that a selected-by-default option has a working selected property.\n // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)\n optSelected: opt.selected,\n\n // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)\n getSetAttribute: div.className !== \"t\",\n\n // Tests for enctype support on a form (#6743)\n enctype: !!document.createElement(\"form\").enctype,\n\n // Makes sure cloning an html5 element does not cause problems\n // Where outerHTML is undefined, this still works\n html5Clone: document.createElement(\"nav\").cloneNode( true ).outerHTML !== \"<:nav></:nav>\",\n\n // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode\n boxModel: ( document.compatMode === \"CSS1Compat\" ),\n\n // Will be defined later\n submitBubbles: true,\n changeBubbles: true,\n focusinBubbles: false,\n deleteExpando: true,\n noCloneEvent: true,\n inlineBlockNeedsLayout: false,\n shrinkWrapBlocks: false,\n reliableMarginRight: true,\n boxSizingReliable: true,\n pixelPosition: false\n };\n\n // Make sure checked status is properly cloned\n input.checked = true;\n support.noCloneChecked = input.cloneNode( true ).checked;\n\n // Make sure that the options inside disabled selects aren't marked as disabled\n // (WebKit marks them as disabled)\n select.disabled = true;\n support.optDisabled = !opt.disabled;\n\n // Test to see if it's possible to delete an expando from an element\n // Fails in Internet Explorer\n try {\n delete div.test;\n } catch( e ) {\n support.deleteExpando = false;\n }\n\n if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {\n div.attachEvent( \"onclick\", clickFn = function() {\n // Cloning a node shouldn't copy over any\n // bound event handlers (IE does this)\n support.noCloneEvent = false;\n });\n div.cloneNode( true ).fireEvent(\"onclick\");\n div.detachEvent( \"onclick\", clickFn );\n }\n\n // Check if a radio maintains its value\n // after being appended to the DOM\n input = document.createElement(\"input\");\n input.value = \"t\";\n input.setAttribute( \"type\", \"radio\" );\n support.radioValue = input.value === \"t\";\n\n input.setAttribute( \"checked\", \"checked\" );\n\n // #11217 - WebKit loses check when the name is after the checked attribute\n input.setAttribute( \"name\", \"t\" );\n\n div.appendChild( input );\n fragment = document.createDocumentFragment();\n fragment.appendChild( div.lastChild );\n\n // WebKit doesn't clone checked state correctly in fragments\n support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n // Check if a disconnected checkbox will retain its checked\n // value of true after appended to the DOM (IE6/7)\n support.appendChecked = input.checked;\n\n fragment.removeChild( input );\n fragment.appendChild( div );\n\n // Technique from Juriy Zaytsev\n // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/\n // We only care about the case where non-standard event systems\n // are used, namely in IE. Short-circuiting here helps us to\n // avoid an eval call (in setAttribute) which can cause CSP\n // to go haywire. See: https://developer.mozilla.org/en/Security/CSP\n if ( div.attachEvent ) {\n for ( i in {\n submit: true,\n change: true,\n focusin: true\n }) {\n eventName = \"on\" + i;\n isSupported = ( eventName in div );\n if ( !isSupported ) {\n div.setAttribute( eventName, \"return;\" );\n isSupported = ( typeof div[ eventName ] === \"function\" );\n }\n support[ i + \"Bubbles\" ] = isSupported;\n }\n }\n\n // Run tests that need a body at doc ready\n jQuery(function() {\n var container, div, tds, marginDiv,\n divReset = \"padding:0;margin:0;border:0;display:block;overflow:hidden;\",\n body = document.getElementsByTagName(\"body\")[0];\n\n if ( !body ) {\n // Return for frameset docs that don't have a body\n return;\n }\n\n container = document.createElement(\"div\");\n container.style.cssText = \"visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px\";\n body.insertBefore( container, body.firstChild );\n\n // Construct the test element\n div = document.createElement(\"div\");\n container.appendChild( div );\n\n // Check if table cells still have offsetWidth/Height when they are set\n // to display:none and there are still other visible table cells in a\n // table row; if so, offsetWidth/Height are not reliable for use when\n // determining if an element has been hidden directly using\n // display:none (it is still safe to use offsets if a parent element is\n // hidden; don safety goggles and see bug #4512 for more information).\n // (only IE 8 fails this test)\n div.innerHTML = \"<table><tr><td></td><td>t</td></tr></table>\";\n tds = div.getElementsByTagName(\"td\");\n tds[ 0 ].style.cssText = \"padding:0;margin:0;border:0;display:none\";\n isSupported = ( tds[ 0 ].offsetHeight === 0 );\n\n tds[ 0 ].style.display = \"\";\n tds[ 1 ].style.display = \"none\";\n\n // Check if empty table cells still have offsetWidth/Height\n // (IE <= 8 fail this test)\n support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );\n\n // Check box-sizing and margin behavior\n div.innerHTML = \"\";\n div.style.cssText = \"box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;\";\n support.boxSizing = ( div.offsetWidth === 4 );\n support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );\n\n // NOTE: To any future maintainer, we've window.getComputedStyle\n // because jsdom on node.js will break without it.\n if ( window.getComputedStyle ) {\n support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== \"1%\";\n support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: \"4px\" } ).width === \"4px\";\n\n // Check if div with explicit width and no margin-right incorrectly\n // gets computed margin-right based on width of container. For more\n // info see bug #3333\n // Fails in WebKit before Feb 2011 nightlies\n // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right\n marginDiv = document.createElement(\"div\");\n marginDiv.style.cssText = div.style.cssText = divReset;\n marginDiv.style.marginRight = marginDiv.style.width = \"0\";\n div.style.width = \"1px\";\n div.appendChild( marginDiv );\n support.reliableMarginRight =\n !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );\n }\n\n if ( typeof div.style.zoom !== \"undefined\" ) {\n // Check if natively block-level elements act like inline-block\n // elements when setting their display to 'inline' and giving\n // them layout\n // (IE < 8 does this)\n div.innerHTML = \"\";\n div.style.cssText = divReset + \"width:1px;padding:1px;display:inline;zoom:1\";\n support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );\n\n // Check if elements with layout shrink-wrap their children\n // (IE 6 does this)\n div.style.display = \"block\";\n div.style.overflow = \"visible\";\n div.innerHTML = \"<div></div>\";\n div.firstChild.style.width = \"5px\";\n support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );\n\n container.style.zoom = 1;\n }\n\n // Null elements to avoid leaks in IE\n body.removeChild( container );\n container = div = tds = marginDiv = null;\n });\n\n // Null elements to avoid leaks in IE\n fragment.removeChild( div );\n all = a = select = opt = input = fragment = div = null;\n\n return support;\n})();\nvar rbrace = /(?:\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\])$/,\n rmultiDash = /([A-Z])/g;\n\njQuery.extend({\n cache: {},\n\n deletedIds: [],\n\n // Remove at next major release (1.9/2.0)\n uuid: 0,\n\n // Unique for each copy of jQuery on the page\n // Non-digits removed to match rinlinejQuery\n expando: \"jQuery\" + ( jQuery.fn.jquery + Math.random() ).replace( /\\D/g, \"\" ),\n\n // The following elements throw uncatchable exceptions if you\n // attempt to add expando properties to them.\n noData: {\n \"embed\": true,\n // Ban all objects except for Flash (which handle expandos)\n \"object\": \"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\",\n \"applet\": true\n },\n\n hasData: function( elem ) {\n elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];\n return !!elem && !isEmptyDataObject( elem );\n },\n\n data: function( elem, name, data, pvt /* Internal Use Only */ ) {\n if ( !jQuery.acceptData( elem ) ) {\n return;\n }\n\n var thisCache, ret,\n internalKey = jQuery.expando,\n getByName = typeof name === \"string\",\n\n // We have to handle DOM nodes and JS objects differently because IE6-7\n // can't GC object references properly across the DOM-JS boundary\n isNode = elem.nodeType,\n\n // Only DOM nodes need the global jQuery cache; JS object data is\n // attached directly to the object so GC can occur automatically\n cache = isNode ? jQuery.cache : elem,\n\n // Only defining an ID for JS objects if its cache already exists allows\n // the code to shortcut on the same path as a DOM node with no cache\n id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;\n\n // Avoid doing any more work than we need to when trying to get data on an\n // object that has no data at all\n if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {\n return;\n }\n\n if ( !id ) {\n // Only DOM nodes need a new unique ID for each element since their data\n // ends up in the global cache\n if ( isNode ) {\n elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;\n } else {\n id = internalKey;\n }\n }\n\n if ( !cache[ id ] ) {\n cache[ id ] = {};\n\n // Avoids exposing jQuery metadata on plain JS objects when the object\n // is serialized using JSON.stringify\n if ( !isNode ) {\n cache[ id ].toJSON = jQuery.noop;\n }\n }\n\n // An object can be passed to jQuery.data instead of a key/value pair; this gets\n // shallow copied over onto the existing cache\n if ( typeof name === \"object\" || typeof name === \"function\" ) {\n if ( pvt ) {\n cache[ id ] = jQuery.extend( cache[ id ], name );\n } else {\n cache[ id ].data = jQuery.extend( cache[ id ].data, name );\n }\n }\n\n thisCache = cache[ id ];\n\n // jQuery data() is stored in a separate object inside the object's internal data\n // cache in order to avoid key collisions between internal data and user-defined\n // data.\n if ( !pvt ) {\n if ( !thisCache.data ) {\n thisCache.data = {};\n }\n\n thisCache = thisCache.data;\n }\n\n if ( data !== undefined ) {\n thisCache[ jQuery.camelCase( name ) ] = data;\n }\n\n // Check for both converted-to-camel and non-converted data property names\n // If a data property was specified\n if ( getByName ) {\n\n // First Try to find as-is property data\n ret = thisCache[ name ];\n\n // Test for null|undefined property data\n if ( ret == null ) {\n\n // Try to find the camelCased property\n ret = thisCache[ jQuery.camelCase( name ) ];\n }\n } else {\n ret = thisCache;\n }\n\n return ret;\n },\n\n removeData: function( elem, name, pvt /* Internal Use Only */ ) {\n if ( !jQuery.acceptData( elem ) ) {\n return;\n }\n\n var thisCache, i, l,\n\n isNode = elem.nodeType,\n\n // See jQuery.data for more information\n cache = isNode ? jQuery.cache : elem,\n id = isNode ? elem[ jQuery.expando ] : jQuery.expando;\n\n // If there is already no cache entry for this object, there is no\n // purpose in continuing\n if ( !cache[ id ] ) {\n return;\n }\n\n if ( name ) {\n\n thisCache = pvt ? cache[ id ] : cache[ id ].data;\n\n if ( thisCache ) {\n\n // Support array or space separated string names for data keys\n if ( !jQuery.isArray( name ) ) {\n\n // try the string as a key before any manipulation\n if ( name in thisCache ) {\n name = [ name ];\n } else {\n\n // split the camel cased version by spaces unless a key with the spaces exists\n name = jQuery.camelCase( name );\n if ( name in thisCache ) {\n name = [ name ];\n } else {\n name = name.split(\" \");\n }\n }\n }\n\n for ( i = 0, l = name.length; i < l; i++ ) {\n delete thisCache[ name[i] ];\n }\n\n // If there is no data left in the cache, we want to continue\n // and let the cache object itself get destroyed\n if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {\n return;\n }\n }\n }\n\n // See jQuery.data for more information\n if ( !pvt ) {\n delete cache[ id ].data;\n\n // Don't destroy the parent cache unless the internal data object\n // had been the only thing left in it\n if ( !isEmptyDataObject( cache[ id ] ) ) {\n return;\n }\n }\n\n // Destroy the cache\n if ( isNode ) {\n jQuery.cleanData( [ elem ], true );\n\n // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)\n } else if ( jQuery.support.deleteExpando || cache != cache.window ) {\n delete cache[ id ];\n\n // When all else fails, null\n } else {\n cache[ id ] = null;\n }\n },\n\n // For internal use only.\n _data: function( elem, name, data ) {\n return jQuery.data( elem, name, data, true );\n },\n\n // A method for determining if a DOM node can handle the data expando\n acceptData: function( elem ) {\n var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];\n\n // nodes accept data unless otherwise specified; rejection can be conditional\n return !noData || noData !== true && elem.getAttribute(\"classid\") === noData;\n }\n});\n\njQuery.fn.extend({\n data: function( key, value ) {\n var parts, part, attr, name, l,\n elem = this[0],\n i = 0,\n data = null;\n\n // Gets all values\n if ( key === undefined ) {\n if ( this.length ) {\n data = jQuery.data( elem );\n\n if ( elem.nodeType === 1 && !jQuery._data( elem, \"parsedAttrs\" ) ) {\n attr = elem.attributes;\n for ( l = attr.length; i < l; i++ ) {\n name = attr[i].name;\n\n if ( !name.indexOf( \"data-\" ) ) {\n name = jQuery.camelCase( name.substring(5) );\n\n dataAttr( elem, name, data[ name ] );\n }\n }\n jQuery._data( elem, \"parsedAttrs\", true );\n }\n }\n\n return data;\n }\n\n // Sets multiple values\n if ( typeof key === \"object\" ) {\n return this.each(function() {\n jQuery.data( this, key );\n });\n }\n\n parts = key.split( \".\", 2 );\n parts[1] = parts[1] ? \".\" + parts[1] : \"\";\n part = parts[1] + \"!\";\n\n return jQuery.access( this, function( value ) {\n\n if ( value === undefined ) {\n data = this.triggerHandler( \"getData\" + part, [ parts[0] ] );\n\n // Try to fetch any internally stored data first\n if ( data === undefined && elem ) {\n data = jQuery.data( elem, key );\n data = dataAttr( elem, key, data );\n }\n\n return data === undefined && parts[1] ?\n this.data( parts[0] ) :\n data;\n }\n\n parts[1] = value;\n this.each(function() {\n var self = jQuery( this );\n\n self.triggerHandler( \"setData\" + part, parts );\n jQuery.data( this, key, value );\n self.triggerHandler( \"changeData\" + part, parts );\n });\n }, null, value, arguments.length > 1, null, false );\n },\n\n removeData: function( key ) {\n return this.each(function() {\n jQuery.removeData( this, key );\n });\n }\n});\n\nfunction dataAttr( elem, key, data ) {\n // If nothing was found internally, try to fetch any\n // data from the HTML5 data-* attribute\n if ( data === undefined && elem.nodeType === 1 ) {\n\n var name = \"data-\" + key.replace( rmultiDash, \"-$1\" ).toLowerCase();\n\n data = elem.getAttribute( name );\n\n if ( typeof data === \"string\" ) {\n try {\n data = data === \"true\" ? true :\n data === \"false\" ? false :\n data === \"null\" ? null :\n // Only convert to a number if it doesn't change the string\n +data + \"\" === data ? +data :\n rbrace.test( data ) ? jQuery.parseJSON( data ) :\n data;\n } catch( e ) {}\n\n // Make sure we set the data so it isn't changed later\n jQuery.data( elem, key, data );\n\n } else {\n data = undefined;\n }\n }\n\n return data;\n}\n\n// checks a cache object for emptiness\nfunction isEmptyDataObject( obj ) {\n var name;\n for ( name in obj ) {\n\n // if the public data object is empty, the private is still empty\n if ( name === \"data\" && jQuery.isEmptyObject( obj[name] ) ) {\n continue;\n }\n if ( name !== \"toJSON\" ) {\n return false;\n }\n }\n\n return true;\n}\njQuery.extend({\n queue: function( elem, type, data ) {\n var queue;\n\n if ( elem ) {\n type = ( type || \"fx\" ) + \"queue\";\n queue = jQuery._data( elem, type );\n\n // Speed up dequeue by getting out quickly if this is just a lookup\n if ( data ) {\n if ( !queue || jQuery.isArray(data) ) {\n queue = jQuery._data( elem, type, jQuery.makeArray(data) );\n } else {\n queue.push( data );\n }\n }\n return queue || [];\n }\n },\n\n dequeue: function( elem, type ) {\n type = type || \"fx\";\n\n var queue = jQuery.queue( elem, type ),\n startLength = queue.length,\n fn = queue.shift(),\n hooks = jQuery._queueHooks( elem, type ),\n next = function() {\n jQuery.dequeue( elem, type );\n };\n\n // If the fx queue is dequeued, always remove the progress sentinel\n if ( fn === \"inprogress\" ) {\n fn = queue.shift();\n startLength--;\n }\n\n if ( fn ) {\n\n // Add a progress sentinel to prevent the fx queue from being\n // automatically dequeued\n if ( type === \"fx\" ) {\n queue.unshift( \"inprogress\" );\n }\n\n // clear up the last queue stop function\n delete hooks.stop;\n fn.call( elem, next, hooks );\n }\n\n if ( !startLength && hooks ) {\n hooks.empty.fire();\n }\n },\n\n // not intended for public consumption - generates a queueHooks object, or returns the current one\n _queueHooks: function( elem, type ) {\n var key = type + \"queueHooks\";\n return jQuery._data( elem, key ) || jQuery._data( elem, key, {\n empty: jQuery.Callbacks(\"once memory\").add(function() {\n jQuery.removeData( elem, type + \"queue\", true );\n jQuery.removeData( elem, key, true );\n })\n });\n }\n});\n\njQuery.fn.extend({\n queue: function( type, data ) {\n var setter = 2;\n\n if ( typeof type !== \"string\" ) {\n data = type;\n type = \"fx\";\n setter--;\n }\n\n if ( arguments.length < setter ) {\n return jQuery.queue( this[0], type );\n }\n\n return data === undefined ?\n this :\n this.each(function() {\n var queue = jQuery.queue( this, type, data );\n\n // ensure a hooks for this queue\n jQuery._queueHooks( this, type );\n\n if ( type === \"fx\" && queue[0] !== \"inprogress\" ) {\n jQuery.dequeue( this, type );\n }\n });\n },\n dequeue: function( type ) {\n return this.each(function() {\n jQuery.dequeue( this, type );\n });\n },\n // Based off of the plugin by Clint Helfers, with permission.\n // http://blindsignals.com/index.php/2009/07/jquery-delay/\n delay: function( time, type ) {\n time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n type = type || \"fx\";\n\n return this.queue( type, function( next, hooks ) {\n var timeout = setTimeout( next, time );\n hooks.stop = function() {\n clearTimeout( timeout );\n };\n });\n },\n clearQueue: function( type ) {\n return this.queue( type || \"fx\", [] );\n },\n // Get a promise resolved when queues of a certain type\n // are emptied (fx is the type by default)\n promise: function( type, obj ) {\n var tmp,\n count = 1,\n defer = jQuery.Deferred(),\n elements = this,\n i = this.length,\n resolve = function() {\n if ( !( --count ) ) {\n defer.resolveWith( elements, [ elements ] );\n }\n };\n\n if ( typeof type !== \"string\" ) {\n obj = type;\n type = undefined;\n }\n type = type || \"fx\";\n\n while( i-- ) {\n tmp = jQuery._data( elements[ i ], type + \"queueHooks\" );\n if ( tmp && tmp.empty ) {\n count++;\n tmp.empty.add( resolve );\n }\n }\n resolve();\n return defer.promise( obj );\n }\n});\nvar nodeHook, boolHook, fixSpecified,\n rclass = /[\\t\\r\\n]/g,\n rreturn = /\\r/g,\n rtype = /^(?:button|input)$/i,\n rfocusable = /^(?:button|input|object|select|textarea)$/i,\n rclickable = /^a(?:rea|)$/i,\n rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,\n getSetAttribute = jQuery.support.getSetAttribute;\n\njQuery.fn.extend({\n attr: function( name, value ) {\n return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );\n },\n\n removeAttr: function( name ) {\n return this.each(function() {\n jQuery.removeAttr( this, name );\n });\n },\n\n prop: function( name, value ) {\n return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );\n },\n\n removeProp: function( name ) {\n name = jQuery.propFix[ name ] || name;\n return this.each(function() {\n // try/catch handles cases where IE balks (such as removing a property on window)\n try {\n this[ name ] = undefined;\n delete this[ name ];\n } catch( e ) {}\n });\n },\n\n addClass: function( value ) {\n var classNames, i, l, elem,\n setClass, c, cl;\n\n if ( jQuery.isFunction( value ) ) {\n return this.each(function( j ) {\n jQuery( this ).addClass( value.call(this, j, this.className) );\n });\n }\n\n if ( value && typeof value === \"string\" ) {\n classNames = value.split( core_rspace );\n\n for ( i = 0, l = this.length; i < l; i++ ) {\n elem = this[ i ];\n\n if ( elem.nodeType === 1 ) {\n if ( !elem.className && classNames.length === 1 ) {\n elem.className = value;\n\n } else {\n setClass = \" \" + elem.className + \" \";\n\n for ( c = 0, cl = classNames.length; c < cl; c++ ) {\n if ( setClass.indexOf( \" \" + classNames[ c ] + \" \" ) < 0 ) {\n setClass += classNames[ c ] + \" \";\n }\n }\n elem.className = jQuery.trim( setClass );\n }\n }\n }\n }\n\n return this;\n },\n\n removeClass: function( value ) {\n var removes, className, elem, c, cl, i, l;\n\n if ( jQuery.isFunction( value ) ) {\n return this.each(function( j ) {\n jQuery( this ).removeClass( value.call(this, j, this.className) );\n });\n }\n if ( (value && typeof value === \"string\") || value === undefined ) {\n removes = ( value || \"\" ).split( core_rspace );\n\n for ( i = 0, l = this.length; i < l; i++ ) {\n elem = this[ i ];\n if ( elem.nodeType === 1 && elem.className ) {\n\n className = (\" \" + elem.className + \" \").replace( rclass, \" \" );\n\n // loop over each item in the removal list\n for ( c = 0, cl = removes.length; c < cl; c++ ) {\n // Remove until there is nothing to remove,\n while ( className.indexOf(\" \" + removes[ c ] + \" \") >= 0 ) {\n className = className.replace( \" \" + removes[ c ] + \" \" , \" \" );\n }\n }\n elem.className = value ? jQuery.trim( className ) : \"\";\n }\n }\n }\n\n return this;\n },\n\n toggleClass: function( value, stateVal ) {\n var type = typeof value,\n isBool = typeof stateVal === \"boolean\";\n\n if ( jQuery.isFunction( value ) ) {\n return this.each(function( i ) {\n jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );\n });\n }\n\n return this.each(function() {\n if ( type === \"string\" ) {\n // toggle individual class names\n var className,\n i = 0,\n self = jQuery( this ),\n state = stateVal,\n classNames = value.split( core_rspace );\n\n while ( (className = classNames[ i++ ]) ) {\n // check each className given, space separated list\n state = isBool ? state : !self.hasClass( className );\n self[ state ? \"addClass\" : \"removeClass\" ]( className );\n }\n\n } else if ( type === \"undefined\" || type === \"boolean\" ) {\n if ( this.className ) {\n // store className if set\n jQuery._data( this, \"__className__\", this.className );\n }\n\n // toggle whole className\n this.className = this.className || value === false ? \"\" : jQuery._data( this, \"__className__\" ) || \"\";\n }\n });\n },\n\n hasClass: function( selector ) {\n var className = \" \" + selector + \" \",\n i = 0,\n l = this.length;\n for ( ; i < l; i++ ) {\n if ( this[i].nodeType === 1 && (\" \" + this[i].className + \" \").replace(rclass, \" \").indexOf( className ) >= 0 ) {\n return true;\n }\n }\n\n return false;\n },\n\n val: function( value ) {\n var hooks, ret, isFunction,\n elem = this[0];\n\n if ( !arguments.length ) {\n if ( elem ) {\n hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];\n\n if ( hooks && \"get\" in hooks && (ret = hooks.get( elem, \"value\" )) !== undefined ) {\n return ret;\n }\n\n ret = elem.value;\n\n return typeof ret === \"string\" ?\n // handle most common string cases\n ret.replace(rreturn, \"\") :\n // handle cases where value is null/undef or number\n ret == null ? \"\" : ret;\n }\n\n return;\n }\n\n isFunction = jQuery.isFunction( value );\n\n return this.each(function( i ) {\n var val,\n self = jQuery(this);\n\n if ( this.nodeType !== 1 ) {\n return;\n }\n\n if ( isFunction ) {\n val = value.call( this, i, self.val() );\n } else {\n val = value;\n }\n\n // Treat null/undefined as \"\"; convert numbers to string\n if ( val == null ) {\n val = \"\";\n } else if ( typeof val === \"number\" ) {\n val += \"\";\n } else if ( jQuery.isArray( val ) ) {\n val = jQuery.map(val, function ( value ) {\n return value == null ? \"\" : value + \"\";\n });\n }\n\n hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];\n\n // If set returns undefined, fall back to normal setting\n if ( !hooks || !(\"set\" in hooks) || hooks.set( this, val, \"value\" ) === undefined ) {\n this.value = val;\n }\n });\n }\n});\n\njQuery.extend({\n valHooks: {\n option: {\n get: function( elem ) {\n // attributes.value is undefined in Blackberry 4.7 but\n // uses .value. See #6932\n var val = elem.attributes.value;\n return !val || val.specified ? elem.value : elem.text;\n }\n },\n select: {\n get: function( elem ) {\n var value, option,\n options = elem.options,\n index = elem.selectedIndex,\n one = elem.type === \"select-one\" || index < 0,\n values = one ? null : [],\n max = one ? index + 1 : options.length,\n i = index < 0 ?\n max :\n one ? index : 0;\n\n // Loop through all the selected options\n for ( ; i < max; i++ ) {\n option = options[ i ];\n\n // oldIE doesn't update selected after form reset (#2551)\n if ( ( option.selected || i === index ) &&\n // Don't return options that are disabled or in a disabled optgroup\n ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute(\"disabled\") === null ) &&\n ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, \"optgroup\" ) ) ) {\n\n // Get the specific value for the option\n value = jQuery( option ).val();\n\n // We don't need an array for one selects\n if ( one ) {\n return value;\n }\n\n // Multi-Selects return an array\n values.push( value );\n }\n }\n\n return values;\n },\n\n set: function( elem, value ) {\n var values = jQuery.makeArray( value );\n\n jQuery(elem).find(\"option\").each(function() {\n this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;\n });\n\n if ( !values.length ) {\n elem.selectedIndex = -1;\n }\n return values;\n }\n }\n },\n\n // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9\n attrFn: {},\n\n attr: function( elem, name, value, pass ) {\n var ret, hooks, notxml,\n nType = elem.nodeType;\n\n // don't get/set attributes on text, comment and attribute nodes\n if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {\n return;\n }\n\n if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {\n return jQuery( elem )[ name ]( value );\n }\n\n // Fallback to prop when attributes are not supported\n if ( typeof elem.getAttribute === \"undefined\" ) {\n return jQuery.prop( elem, name, value );\n }\n\n notxml = nType !== 1 || !jQuery.isXMLDoc( elem );\n\n // All attributes are lowercase\n // Grab necessary hook if one is defined\n if ( notxml ) {\n name = name.toLowerCase();\n hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );\n }\n\n if ( value !== undefined ) {\n\n if ( value === null ) {\n jQuery.removeAttr( elem, name );\n return;\n\n } else if ( hooks && \"set\" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {\n return ret;\n\n } else {\n elem.setAttribute( name, value + \"\" );\n return value;\n }\n\n } else if ( hooks && \"get\" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {\n return ret;\n\n } else {\n\n ret = elem.getAttribute( name );\n\n // Non-existent attributes return null, we normalize to undefined\n return ret === null ?\n undefined :\n ret;\n }\n },\n\n removeAttr: function( elem, value ) {\n var propName, attrNames, name, isBool,\n i = 0;\n\n if ( value && elem.nodeType === 1 ) {\n\n attrNames = value.split( core_rspace );\n\n for ( ; i < attrNames.length; i++ ) {\n name = attrNames[ i ];\n\n if ( name ) {\n propName = jQuery.propFix[ name ] || name;\n isBool = rboolean.test( name );\n\n // See #9699 for explanation of this approach (setting first, then removal)\n // Do not do this for boolean attributes (see #10870)\n if ( !isBool ) {\n jQuery.attr( elem, name, \"\" );\n }\n elem.removeAttribute( getSetAttribute ? name : propName );\n\n // Set corresponding property to false for boolean attributes\n if ( isBool && propName in elem ) {\n elem[ propName ] = false;\n }\n }\n }\n }\n },\n\n attrHooks: {\n type: {\n set: function( elem, value ) {\n // We can't allow the type property to be changed (since it causes problems in IE)\n if ( rtype.test( elem.nodeName ) && elem.parentNode ) {\n jQuery.error( \"type property can't be changed\" );\n } else if ( !jQuery.support.radioValue && value === \"radio\" && jQuery.nodeName(elem, \"input\") ) {\n // Setting the type on a radio button after the value resets the value in IE6-9\n // Reset value to it's default in case type is set after value\n // This is for element creation\n var val = elem.value;\n elem.setAttribute( \"type\", value );\n if ( val ) {\n elem.value = val;\n }\n return value;\n }\n }\n },\n // Use the value property for back compat\n // Use the nodeHook for button elements in IE6/7 (#1954)\n value: {\n get: function( elem, name ) {\n if ( nodeHook && jQuery.nodeName( elem, \"button\" ) ) {\n return nodeHook.get( elem, name );\n }\n return name in elem ?\n elem.value :\n null;\n },\n set: function( elem, value, name ) {\n if ( nodeHook && jQuery.nodeName( elem, \"button\" ) ) {\n return nodeHook.set( elem, value, name );\n }\n // Does not return so that setAttribute is also used\n elem.value = value;\n }\n }\n },\n\n propFix: {\n tabindex: \"tabIndex\",\n readonly: \"readOnly\",\n \"for\": \"htmlFor\",\n \"class\": \"className\",\n maxlength: \"maxLength\",\n cellspacing: \"cellSpacing\",\n cellpadding: \"cellPadding\",\n rowspan: \"rowSpan\",\n colspan: \"colSpan\",\n usemap: \"useMap\",\n frameborder: \"frameBorder\",\n contenteditable: \"contentEditable\"\n },\n\n prop: function( elem, name, value ) {\n var ret, hooks, notxml,\n nType = elem.nodeType;\n\n // don't get/set properties on text, comment and attribute nodes\n if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {\n return;\n }\n\n notxml = nType !== 1 || !jQuery.isXMLDoc( elem );\n\n if ( notxml ) {\n // Fix name and attach hooks\n name = jQuery.propFix[ name ] || name;\n hooks = jQuery.propHooks[ name ];\n }\n\n if ( value !== undefined ) {\n if ( hooks && \"set\" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {\n return ret;\n\n } else {\n return ( elem[ name ] = value );\n }\n\n } else {\n if ( hooks && \"get\" in hooks && (ret = hooks.get( elem, name )) !== null ) {\n return ret;\n\n } else {\n return elem[ name ];\n }\n }\n },\n\n propHooks: {\n tabIndex: {\n get: function( elem ) {\n // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set\n // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n var attributeNode = elem.getAttributeNode(\"tabindex\");\n\n return attributeNode && attributeNode.specified ?\n parseInt( attributeNode.value, 10 ) :\n rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?\n 0 :\n undefined;\n }\n }\n }\n});\n\n// Hook for boolean attributes\nboolHook = {\n get: function( elem, name ) {\n // Align boolean attributes with corresponding properties\n // Fall back to attribute presence where some booleans are not supported\n var attrNode,\n property = jQuery.prop( elem, name );\n return property === true || typeof property !== \"boolean\" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?\n name.toLowerCase() :\n undefined;\n },\n set: function( elem, value, name ) {\n var propName;\n if ( value === false ) {\n // Remove boolean attributes when set to false\n jQuery.removeAttr( elem, name );\n } else {\n // value is true since we know at this point it's type boolean and not false\n // Set boolean attributes to the same name and set the DOM property\n propName = jQuery.propFix[ name ] || name;\n if ( propName in elem ) {\n // Only set the IDL specifically if it already exists on the element\n elem[ propName ] = true;\n }\n\n elem.setAttribute( name, name.toLowerCase() );\n }\n return name;\n }\n};\n\n// IE6/7 do not support getting/setting some attributes with get/setAttribute\nif ( !getSetAttribute ) {\n\n fixSpecified = {\n name: true,\n id: true,\n coords: true\n };\n\n // Use this for any attribute in IE6/7\n // This fixes almost every IE6/7 issue\n nodeHook = jQuery.valHooks.button = {\n get: function( elem, name ) {\n var ret;\n ret = elem.getAttributeNode( name );\n return ret && ( fixSpecified[ name ] ? ret.value !== \"\" : ret.specified ) ?\n ret.value :\n undefined;\n },\n set: function( elem, value, name ) {\n // Set the existing or create a new attribute node\n var ret = elem.getAttributeNode( name );\n if ( !ret ) {\n ret = document.createAttribute( name );\n elem.setAttributeNode( ret );\n }\n return ( ret.value = value + \"\" );\n }\n };\n\n // Set width and height to auto instead of 0 on empty string( Bug #8150 )\n // This is for removals\n jQuery.each([ \"width\", \"height\" ], function( i, name ) {\n jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {\n set: function( elem, value ) {\n if ( value === \"\" ) {\n elem.setAttribute( name, \"auto\" );\n return value;\n }\n }\n });\n });\n\n // Set contenteditable to false on removals(#10429)\n // Setting to empty string throws an error as an invalid value\n jQuery.attrHooks.contenteditable = {\n get: nodeHook.get,\n set: function( elem, value, name ) {\n if ( value === \"\" ) {\n value = \"false\";\n }\n nodeHook.set( elem, value, name );\n }\n };\n}\n\n\n// Some attributes require a special call on IE\nif ( !jQuery.support.hrefNormalized ) {\n jQuery.each([ \"href\", \"src\", \"width\", \"height\" ], function( i, name ) {\n jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {\n get: function( elem ) {\n var ret = elem.getAttribute( name, 2 );\n return ret === null ? undefined : ret;\n }\n });\n });\n}\n\nif ( !jQuery.support.style ) {\n jQuery.attrHooks.style = {\n get: function( elem ) {\n // Return undefined in the case of empty string\n // Normalize to lowercase since IE uppercases css property names\n return elem.style.cssText.toLowerCase() || undefined;\n },\n set: function( elem, value ) {\n return ( elem.style.cssText = value + \"\" );\n }\n };\n}\n\n// Safari mis-reports the default selected property of an option\n// Accessing the parent's selectedIndex property fixes it\nif ( !jQuery.support.optSelected ) {\n jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {\n get: function( elem ) {\n var parent = elem.parentNode;\n\n if ( parent ) {\n parent.selectedIndex;\n\n // Make sure that it also works with optgroups, see #5701\n if ( parent.parentNode ) {\n parent.parentNode.selectedIndex;\n }\n }\n return null;\n }\n });\n}\n\n// IE6/7 call enctype encoding\nif ( !jQuery.support.enctype ) {\n jQuery.propFix.enctype = \"encoding\";\n}\n\n// Radios and checkboxes getter/setter\nif ( !jQuery.support.checkOn ) {\n jQuery.each([ \"radio\", \"checkbox\" ], function() {\n jQuery.valHooks[ this ] = {\n get: function( elem ) {\n // Handle the case where in Webkit \"\" is returned instead of \"on\" if a value isn't specified\n return elem.getAttribute(\"value\") === null ? \"on\" : elem.value;\n }\n };\n });\n}\njQuery.each([ \"radio\", \"checkbox\" ], function() {\n jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {\n set: function( elem, value ) {\n if ( jQuery.isArray( value ) ) {\n return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );\n }\n }\n });\n});\nvar rformElems = /^(?:textarea|input|select)$/i,\n rtypenamespace = /^([^\\.]*|)(?:\\.(.+)|)$/,\n rhoverHack = /(?:^|\\s)hover(\\.\\S+|)\\b/,\n rkeyEvent = /^key/,\n rmouseEvent = /^(?:mouse|contextmenu)|click/,\n rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n hoverHack = function( events ) {\n return jQuery.event.special.hover ? events : events.replace( rhoverHack, \"mouseenter$1 mouseleave$1\" );\n };\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n add: function( elem, types, handler, data, selector ) {\n\n var elemData, eventHandle, events,\n t, tns, type, namespaces, handleObj,\n handleObjIn, handlers, special;\n\n // Don't attach events to noData or text/comment nodes (allow plain objects tho)\n if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {\n return;\n }\n\n // Caller can pass in an object of custom data in lieu of the handler\n if ( handler.handler ) {\n handleObjIn = handler;\n handler = handleObjIn.handler;\n selector = handleObjIn.selector;\n }\n\n // Make sure that the handler has a unique ID, used to find/remove it later\n if ( !handler.guid ) {\n handler.guid = jQuery.guid++;\n }\n\n // Init the element's event structure and main handler, if this is the first\n events = elemData.events;\n if ( !events ) {\n elemData.events = events = {};\n }\n eventHandle = elemData.handle;\n if ( !eventHandle ) {\n elemData.handle = eventHandle = function( e ) {\n // Discard the second event of a jQuery.event.trigger() and\n // when an event is called after a page has unloaded\n return typeof jQuery !== \"undefined\" && (!e || jQuery.event.triggered !== e.type) ?\n jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :\n undefined;\n };\n // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events\n eventHandle.elem = elem;\n }\n\n // Handle multiple events separated by a space\n // jQuery(...).bind(\"mouseover mouseout\", fn);\n types = jQuery.trim( hoverHack(types) ).split( \" \" );\n for ( t = 0; t < types.length; t++ ) {\n\n tns = rtypenamespace.exec( types[t] ) || [];\n type = tns[1];\n namespaces = ( tns[2] || \"\" ).split( \".\" ).sort();\n\n // If event changes its type, use the special event handlers for the changed type\n special = jQuery.event.special[ type ] || {};\n\n // If selector defined, determine special event api type, otherwise given type\n type = ( selector ? special.delegateType : special.bindType ) || type;\n\n // Update special based on newly reset type\n special = jQuery.event.special[ type ] || {};\n\n // handleObj is passed to all event handlers\n handleObj = jQuery.extend({\n type: type,\n origType: tns[1],\n data: data,\n handler: handler,\n guid: handler.guid,\n selector: selector,\n needsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n namespace: namespaces.join(\".\")\n }, handleObjIn );\n\n // Init the event handler queue if we're the first\n handlers = events[ type ];\n if ( !handlers ) {\n handlers = events[ type ] = [];\n handlers.delegateCount = 0;\n\n // Only use addEventListener/attachEvent if the special events handler returns false\n if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n // Bind the global event handler to the element\n if ( elem.addEventListener ) {\n elem.addEventListener( type, eventHandle, false );\n\n } else if ( elem.attachEvent ) {\n elem.attachEvent( \"on\" + type, eventHandle );\n }\n }\n }\n\n if ( special.add ) {\n special.add.call( elem, handleObj );\n\n if ( !handleObj.handler.guid ) {\n handleObj.handler.guid = handler.guid;\n }\n }\n\n // Add to the element's handler list, delegates in front\n if ( selector ) {\n handlers.splice( handlers.delegateCount++, 0, handleObj );\n } else {\n handlers.push( handleObj );\n }\n\n // Keep track of which events have ever been used, for event optimization\n jQuery.event.global[ type ] = true;\n }\n\n // Nullify elem to prevent memory leaks in IE\n elem = null;\n },\n\n global: {},\n\n // Detach an event or set of events from an element\n remove: function( elem, types, handler, selector, mappedTypes ) {\n\n var t, tns, type, origType, namespaces, origCount,\n j, events, special, eventType, handleObj,\n elemData = jQuery.hasData( elem ) && jQuery._data( elem );\n\n if ( !elemData || !(events = elemData.events) ) {\n return;\n }\n\n // Once for each type.namespace in types; type may be omitted\n types = jQuery.trim( hoverHack( types || \"\" ) ).split(\" \");\n for ( t = 0; t < types.length; t++ ) {\n tns = rtypenamespace.exec( types[t] ) || [];\n type = origType = tns[1];\n namespaces = tns[2];\n\n // Unbind all events (on this namespace, if provided) for the element\n if ( !type ) {\n for ( type in events ) {\n jQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n }\n continue;\n }\n\n special = jQuery.event.special[ type ] || {};\n type = ( selector? special.delegateType : special.bindType ) || type;\n eventType = events[ type ] || [];\n origCount = eventType.length;\n namespaces = namespaces ? new RegExp(\"(^|\\\\.)\" + namespaces.split(\".\").sort().join(\"\\\\.(?:.*\\\\.|)\") + \"(\\\\.|$)\") : null;\n\n // Remove matching events\n for ( j = 0; j < eventType.length; j++ ) {\n handleObj = eventType[ j ];\n\n if ( ( mappedTypes || origType === handleObj.origType ) &&\n ( !handler || handler.guid === handleObj.guid ) &&\n ( !namespaces || namespaces.test( handleObj.namespace ) ) &&\n ( !selector || selector === handleObj.selector || selector === \"**\" && handleObj.selector ) ) {\n eventType.splice( j--, 1 );\n\n if ( handleObj.selector ) {\n eventType.delegateCount--;\n }\n if ( special.remove ) {\n special.remove.call( elem, handleObj );\n }\n }\n }\n\n // Remove generic event handler if we removed something and no more handlers exist\n // (avoids potential for endless recursion during removal of special event handlers)\n if ( eventType.length === 0 && origCount !== eventType.length ) {\n if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n jQuery.removeEvent( elem, type, elemData.handle );\n }\n\n delete events[ type ];\n }\n }\n\n // Remove the expando if it's no longer used\n if ( jQuery.isEmptyObject( events ) ) {\n delete elemData.handle;\n\n // removeData also checks for emptiness and clears the expando if empty\n // so use it instead of delete\n jQuery.removeData( elem, \"events\", true );\n }\n },\n\n // Events that are safe to short-circuit if no handlers are attached.\n // Native DOM events should not be added, they may have inline handlers.\n customEvent: {\n \"getData\": true,\n \"setData\": true,\n \"changeData\": true\n },\n\n trigger: function( event, data, elem, onlyHandlers ) {\n // Don't do events on text and comment nodes\n if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {\n return;\n }\n\n // Event object or event type\n var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,\n type = event.type || event,\n namespaces = [];\n\n // focus/blur morphs to focusin/out; ensure we're not firing them right now\n if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n return;\n }\n\n if ( type.indexOf( \"!\" ) >= 0 ) {\n // Exclusive events trigger only for the exact event (no namespaces)\n type = type.slice(0, -1);\n exclusive = true;\n }\n\n if ( type.indexOf( \".\" ) >= 0 ) {\n // Namespaced trigger; create a regexp to match event type in handle()\n namespaces = type.split(\".\");\n type = namespaces.shift();\n namespaces.sort();\n }\n\n if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {\n // No jQuery handlers for this event type, and it can't have inline handlers\n return;\n }\n\n // Caller can pass in an Event, Object, or just an event type string\n event = typeof event === \"object\" ?\n // jQuery.Event object\n event[ jQuery.expando ] ? event :\n // Object literal\n new jQuery.Event( type, event ) :\n // Just the event type (string)\n new jQuery.Event( type );\n\n event.type = type;\n event.isTrigger = true;\n event.exclusive = exclusive;\n event.namespace = namespaces.join( \".\" );\n event.namespace_re = event.namespace? new RegExp(\"(^|\\\\.)\" + namespaces.join(\"\\\\.(?:.*\\\\.|)\") + \"(\\\\.|$)\") : null;\n ontype = type.indexOf( \":\" ) < 0 ? \"on\" + type : \"\";\n\n // Handle a global trigger\n if ( !elem ) {\n\n // TODO: Stop taunting the data cache; remove global events and always attach to document\n cache = jQuery.cache;\n for ( i in cache ) {\n if ( cache[ i ].events && cache[ i ].events[ type ] ) {\n jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );\n }\n }\n return;\n }\n\n // Clean up the event in case it is being reused\n event.result = undefined;\n if ( !event.target ) {\n event.target = elem;\n }\n\n // Clone any incoming data and prepend the event, creating the handler arg list\n data = data != null ? jQuery.makeArray( data ) : [];\n data.unshift( event );\n\n // Allow special events to draw outside the lines\n special = jQuery.event.special[ type ] || {};\n if ( special.trigger && special.trigger.apply( elem, data ) === false ) {\n return;\n }\n\n // Determine event propagation path in advance, per W3C events spec (#9951)\n // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n eventPath = [[ elem, special.bindType || type ]];\n if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {\n\n bubbleType = special.delegateType || type;\n cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;\n for ( old = elem; cur; cur = cur.parentNode ) {\n eventPath.push([ cur, bubbleType ]);\n old = cur;\n }\n\n // Only add window if we got to document (e.g., not plain obj or detached DOM)\n if ( old === (elem.ownerDocument || document) ) {\n eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);\n }\n }\n\n // Fire handlers on the event path\n for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {\n\n cur = eventPath[i][0];\n event.type = eventPath[i][1];\n\n handle = ( jQuery._data( cur, \"events\" ) || {} )[ event.type ] && jQuery._data( cur, \"handle\" );\n if ( handle ) {\n handle.apply( cur, data );\n }\n // Note that this is a bare JS function and not a jQuery handler\n handle = ontype && cur[ ontype ];\n if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {\n event.preventDefault();\n }\n }\n event.type = type;\n\n // If nobody prevented the default action, do it now\n if ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&\n !(type === \"click\" && jQuery.nodeName( elem, \"a\" )) && jQuery.acceptData( elem ) ) {\n\n // Call a native DOM method on the target with the same name name as the event.\n // Can't use an .isFunction() check here because IE6/7 fails that test.\n // Don't do default actions on window, that's where global variables be (#6170)\n // IE<9 dies on focus/blur to hidden element (#1486)\n if ( ontype && elem[ type ] && ((type !== \"focus\" && type !== \"blur\") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {\n\n // Don't re-trigger an onFOO event when we call its FOO() method\n old = elem[ ontype ];\n\n if ( old ) {\n elem[ ontype ] = null;\n }\n\n // Prevent re-triggering of the same event, since we already bubbled it above\n jQuery.event.triggered = type;\n elem[ type ]();\n jQuery.event.triggered = undefined;\n\n if ( old ) {\n elem[ ontype ] = old;\n }\n }\n }\n }\n\n return event.result;\n },\n\n dispatch: function( event ) {\n\n // Make a writable jQuery.Event from the native event object\n event = jQuery.event.fix( event || window.event );\n\n var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,\n handlers = ( (jQuery._data( this, \"events\" ) || {} )[ event.type ] || []),\n delegateCount = handlers.delegateCount,\n args = core_slice.call( arguments ),\n run_all = !event.exclusive && !event.namespace,\n special = jQuery.event.special[ event.type ] || {},\n handlerQueue = [];\n\n // Use the fix-ed jQuery.Event rather than the (read-only) native event\n args[0] = event;\n event.delegateTarget = this;\n\n // Call the preDispatch hook for the mapped type, and let it bail if desired\n if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n return;\n }\n\n // Determine handlers that should run if there are delegated events\n // Avoid non-left-click bubbling in Firefox (#3861)\n if ( delegateCount && !(event.button && event.type === \"click\") ) {\n\n for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {\n\n // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)\n if ( cur.disabled !== true || event.type !== \"click\" ) {\n selMatch = {};\n matches = [];\n for ( i = 0; i < delegateCount; i++ ) {\n handleObj = handlers[ i ];\n sel = handleObj.selector;\n\n if ( selMatch[ sel ] === undefined ) {\n selMatch[ sel ] = handleObj.needsContext ?\n jQuery( sel, this ).index( cur ) >= 0 :\n jQuery.find( sel, this, null, [ cur ] ).length;\n }\n if ( selMatch[ sel ] ) {\n matches.push( handleObj );\n }\n }\n if ( matches.length ) {\n handlerQueue.push({ elem: cur, matches: matches });\n }\n }\n }\n }\n\n // Add the remaining (directly-bound) handlers\n if ( handlers.length > delegateCount ) {\n handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });\n }\n\n // Run delegates first; they may want to stop propagation beneath us\n for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {\n matched = handlerQueue[ i ];\n event.currentTarget = matched.elem;\n\n for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {\n handleObj = matched.matches[ j ];\n\n // Triggered event must either 1) be non-exclusive and have no namespace, or\n // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).\n if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {\n\n event.data = handleObj.data;\n event.handleObj = handleObj;\n\n ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )\n .apply( matched.elem, args );\n\n if ( ret !== undefined ) {\n event.result = ret;\n if ( ret === false ) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n }\n }\n }\n\n // Call the postDispatch hook for the mapped type\n if ( special.postDispatch ) {\n special.postDispatch.call( this, event );\n }\n\n return event.result;\n },\n\n // Includes some event props shared by KeyEvent and MouseEvent\n // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***\n props: \"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which\".split(\" \"),\n\n fixHooks: {},\n\n keyHooks: {\n props: \"char charCode key keyCode\".split(\" \"),\n filter: function( event, original ) {\n\n // Add which for key events\n if ( event.which == null ) {\n event.which = original.charCode != null ? original.charCode : original.keyCode;\n }\n\n return event;\n }\n },\n\n mouseHooks: {\n props: \"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement\".split(\" \"),\n filter: function( event, original ) {\n var eventDoc, doc, body,\n button = original.button,\n fromElement = original.fromElement;\n\n // Calculate pageX/Y if missing and clientX/Y available\n if ( event.pageX == null && original.clientX != null ) {\n eventDoc = event.target.ownerDocument || document;\n doc = eventDoc.documentElement;\n body = eventDoc.body;\n\n event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );\n event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );\n }\n\n // Add relatedTarget, if necessary\n if ( !event.relatedTarget && fromElement ) {\n event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;\n }\n\n // Add which for click: 1 === left; 2 === middle; 3 === right\n // Note: button is not normalized, so don't use it\n if ( !event.which && button !== undefined ) {\n event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );\n }\n\n return event;\n }\n },\n\n fix: function( event ) {\n if ( event[ jQuery.expando ] ) {\n return event;\n }\n\n // Create a writable copy of the event object and normalize some properties\n var i, prop,\n originalEvent = event,\n fixHook = jQuery.event.fixHooks[ event.type ] || {},\n copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;\n\n event = jQuery.Event( originalEvent );\n\n for ( i = copy.length; i; ) {\n prop = copy[ --i ];\n event[ prop ] = originalEvent[ prop ];\n }\n\n // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)\n if ( !event.target ) {\n event.target = originalEvent.srcElement || document;\n }\n\n // Target should not be a text node (#504, Safari)\n if ( event.target.nodeType === 3 ) {\n event.target = event.target.parentNode;\n }\n\n // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)\n event.metaKey = !!event.metaKey;\n\n return fixHook.filter? fixHook.filter( event, originalEvent ) : event;\n },\n\n special: {\n load: {\n // Prevent triggered image.load events from bubbling to window.load\n noBubble: true\n },\n\n focus: {\n delegateType: \"focusin\"\n },\n blur: {\n delegateType: \"focusout\"\n },\n\n beforeunload: {\n setup: function( data, namespaces, eventHandle ) {\n // We only want to do this special case on windows\n if ( jQuery.isWindow( this ) ) {\n this.onbeforeunload = eventHandle;\n }\n },\n\n teardown: function( namespaces, eventHandle ) {\n if ( this.onbeforeunload === eventHandle ) {\n this.onbeforeunload = null;\n }\n }\n }\n },\n\n simulate: function( type, elem, event, bubble ) {\n // Piggyback on a donor event to simulate a different one.\n // Fake originalEvent to avoid donor's stopPropagation, but if the\n // simulated event prevents default then we do the same on the donor.\n var e = jQuery.extend(\n new jQuery.Event(),\n event,\n { type: type,\n isSimulated: true,\n originalEvent: {}\n }\n );\n if ( bubble ) {\n jQuery.event.trigger( e, null, elem );\n } else {\n jQuery.event.dispatch.call( elem, e );\n }\n if ( e.isDefaultPrevented() ) {\n event.preventDefault();\n }\n }\n};\n\n// Some plugins are using, but it's undocumented/deprecated and will be removed.\n// The 1.7 special event interface should provide all the hooks needed now.\njQuery.event.handle = jQuery.event.dispatch;\n\njQuery.removeEvent = document.removeEventListener ?\n function( elem, type, handle ) {\n if ( elem.removeEventListener ) {\n elem.removeEventListener( type, handle, false );\n }\n } :\n function( elem, type, handle ) {\n var name = \"on\" + type;\n\n if ( elem.detachEvent ) {\n\n // #8545, #7054, preventing memory leaks for custom events in IE6-8\n // detachEvent needed property on element, by name of that event, to properly expose it to GC\n if ( typeof elem[ name ] === \"undefined\" ) {\n elem[ name ] = null;\n }\n\n elem.detachEvent( name, handle );\n }\n };\n\njQuery.Event = function( src, props ) {\n // Allow instantiation without the 'new' keyword\n if ( !(this instanceof jQuery.Event) ) {\n return new jQuery.Event( src, props );\n }\n\n // Event object\n if ( src && src.type ) {\n this.originalEvent = src;\n this.type = src.type;\n\n // Events bubbling up the document may have been marked as prevented\n // by a handler lower down the tree; reflect the correct value.\n this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||\n src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;\n\n // Event type\n } else {\n this.type = src;\n }\n\n // Put explicitly provided properties onto the event object\n if ( props ) {\n jQuery.extend( this, props );\n }\n\n // Create a timestamp if incoming event doesn't have one\n this.timeStamp = src && src.timeStamp || jQuery.now();\n\n // Mark it as fixed\n this[ jQuery.expando ] = true;\n};\n\nfunction returnFalse() {\n return false;\n}\nfunction returnTrue() {\n return true;\n}\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n preventDefault: function() {\n this.isDefaultPrevented = returnTrue;\n\n var e = this.originalEvent;\n if ( !e ) {\n return;\n }\n\n // if preventDefault exists run it on the original event\n if ( e.preventDefault ) {\n e.preventDefault();\n\n // otherwise set the returnValue property of the original event to false (IE)\n } else {\n e.returnValue = false;\n }\n },\n stopPropagation: function() {\n this.isPropagationStopped = returnTrue;\n\n var e = this.originalEvent;\n if ( !e ) {\n return;\n }\n // if stopPropagation exists run it on the original event\n if ( e.stopPropagation ) {\n e.stopPropagation();\n }\n // otherwise set the cancelBubble property of the original event to true (IE)\n e.cancelBubble = true;\n },\n stopImmediatePropagation: function() {\n this.isImmediatePropagationStopped = returnTrue;\n this.stopPropagation();\n },\n isDefaultPrevented: returnFalse,\n isPropagationStopped: returnFalse,\n isImmediatePropagationStopped: returnFalse\n};\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\njQuery.each({\n mouseenter: \"mouseover\",\n mouseleave: \"mouseout\"\n}, function( orig, fix ) {\n jQuery.event.special[ orig ] = {\n delegateType: fix,\n bindType: fix,\n\n handle: function( event ) {\n var ret,\n target = this,\n related = event.relatedTarget,\n handleObj = event.handleObj,\n selector = handleObj.selector;\n\n // For mousenter/leave call the handler if related is outside the target.\n // NB: No relatedTarget if the mouse left/entered the browser window\n if ( !related || (related !== target && !jQuery.contains( target, related )) ) {\n event.type = handleObj.origType;\n ret = handleObj.handler.apply( this, arguments );\n event.type = fix;\n }\n return ret;\n }\n };\n});\n\n// IE submit delegation\nif ( !jQuery.support.submitBubbles ) {\n\n jQuery.event.special.submit = {\n setup: function() {\n // Only need this for delegated form submit events\n if ( jQuery.nodeName( this, \"form\" ) ) {\n return false;\n }\n\n // Lazy-add a submit handler when a descendant form may potentially be submitted\n jQuery.event.add( this, \"click._submit keypress._submit\", function( e ) {\n // Node name check avoids a VML-related crash in IE (#9807)\n var elem = e.target,\n form = jQuery.nodeName( elem, \"input\" ) || jQuery.nodeName( elem, \"button\" ) ? elem.form : undefined;\n if ( form && !jQuery._data( form, \"_submit_attached\" ) ) {\n jQuery.event.add( form, \"submit._submit\", function( event ) {\n event._submit_bubble = true;\n });\n jQuery._data( form, \"_submit_attached\", true );\n }\n });\n // return undefined since we don't need an event listener\n },\n\n postDispatch: function( event ) {\n // If form was submitted by the user, bubble the event up the tree\n if ( event._submit_bubble ) {\n delete event._submit_bubble;\n if ( this.parentNode && !event.isTrigger ) {\n jQuery.event.simulate( \"submit\", this.parentNode, event, true );\n }\n }\n },\n\n teardown: function() {\n // Only need this for delegated form submit events\n if ( jQuery.nodeName( this, \"form\" ) ) {\n return false;\n }\n\n // Remove delegated handlers; cleanData eventually reaps submit handlers attached above\n jQuery.event.remove( this, \"._submit\" );\n }\n };\n}\n\n// IE change delegation and checkbox/radio fix\nif ( !jQuery.support.changeBubbles ) {\n\n jQuery.event.special.change = {\n\n setup: function() {\n\n if ( rformElems.test( this.nodeName ) ) {\n // IE doesn't fire change on a check/radio until blur; trigger it on click\n // after a propertychange. Eat the blur-change in special.change.handle.\n // This still fires onchange a second time for check/radio after blur.\n if ( this.type === \"checkbox\" || this.type === \"radio\" ) {\n jQuery.event.add( this, \"propertychange._change\", function( event ) {\n if ( event.originalEvent.propertyName === \"checked\" ) {\n this._just_changed = true;\n }\n });\n jQuery.event.add( this, \"click._change\", function( event ) {\n if ( this._just_changed && !event.isTrigger ) {\n this._just_changed = false;\n }\n // Allow triggered, simulated change events (#11500)\n jQuery.event.simulate( \"change\", this, event, true );\n });\n }\n return false;\n }\n // Delegated event; lazy-add a change handler on descendant inputs\n jQuery.event.add( this, \"beforeactivate._change\", function( e ) {\n var elem = e.target;\n\n if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, \"_change_attached\" ) ) {\n jQuery.event.add( elem, \"change._change\", function( event ) {\n if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {\n jQuery.event.simulate( \"change\", this.parentNode, event, true );\n }\n });\n jQuery._data( elem, \"_change_attached\", true );\n }\n });\n },\n\n handle: function( event ) {\n var elem = event.target;\n\n // Swallow native change events from checkbox/radio, we already triggered them above\n if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== \"radio\" && elem.type !== \"checkbox\") ) {\n return event.handleObj.handler.apply( this, arguments );\n }\n },\n\n teardown: function() {\n jQuery.event.remove( this, \"._change\" );\n\n return !rformElems.test( this.nodeName );\n }\n };\n}\n\n// Create \"bubbling\" focus and blur events\nif ( !jQuery.support.focusinBubbles ) {\n jQuery.each({ focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n // Attach a single capturing handler while someone wants focusin/focusout\n var attaches = 0,\n handler = function( event ) {\n jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );\n };\n\n jQuery.event.special[ fix ] = {\n setup: function() {\n if ( attaches++ === 0 ) {\n document.addEventListener( orig, handler, true );\n }\n },\n teardown: function() {\n if ( --attaches === 0 ) {\n document.removeEventListener( orig, handler, true );\n }\n }\n };\n });\n}\n\njQuery.fn.extend({\n\n on: function( types, selector, data, fn, /*INTERNAL*/ one ) {\n var origFn, type;\n\n // Types can be a map of types/handlers\n if ( typeof types === \"object\" ) {\n // ( types-Object, selector, data )\n if ( typeof selector !== \"string\" ) { // && selector != null\n // ( types-Object, data )\n data = data || selector;\n selector = undefined;\n }\n for ( type in types ) {\n this.on( type, selector, data, types[ type ], one );\n }\n return this;\n }\n\n if ( data == null && fn == null ) {\n // ( types, fn )\n fn = selector;\n data = selector = undefined;\n } else if ( fn == null ) {\n if ( typeof selector === \"string\" ) {\n // ( types, selector, fn )\n fn = data;\n data = undefined;\n } else {\n // ( types, data, fn )\n fn = data;\n data = selector;\n selector = undefined;\n }\n }\n if ( fn === false ) {\n fn = returnFalse;\n } else if ( !fn ) {\n return this;\n }\n\n if ( one === 1 ) {\n origFn = fn;\n fn = function( event ) {\n // Can use an empty set, since event contains the info\n jQuery().off( event );\n return origFn.apply( this, arguments );\n };\n // Use same guid so caller can remove using origFn\n fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n }\n return this.each( function() {\n jQuery.event.add( this, types, fn, data, selector );\n });\n },\n one: function( types, selector, data, fn ) {\n return this.on( types, selector, data, fn, 1 );\n },\n off: function( types, selector, fn ) {\n var handleObj, type;\n if ( types && types.preventDefault && types.handleObj ) {\n // ( event ) dispatched jQuery.Event\n handleObj = types.handleObj;\n jQuery( types.delegateTarget ).off(\n handleObj.namespace ? handleObj.origType + \".\" + handleObj.namespace : handleObj.origType,\n handleObj.selector,\n handleObj.handler\n );\n return this;\n }\n if ( typeof types === \"object\" ) {\n // ( types-object [, selector] )\n for ( type in types ) {\n this.off( type, selector, types[ type ] );\n }\n return this;\n }\n if ( selector === false || typeof selector === \"function\" ) {\n // ( types [, fn] )\n fn = selector;\n selector = undefined;\n }\n if ( fn === false ) {\n fn = returnFalse;\n }\n return this.each(function() {\n jQuery.event.remove( this, types, fn, selector );\n });\n },\n\n bind: function( types, data, fn ) {\n return this.on( types, null, data, fn );\n },\n unbind: function( types, fn ) {\n return this.off( types, null, fn );\n },\n\n live: function( types, data, fn ) {\n jQuery( this.context ).on( types, this.selector, data, fn );\n return this;\n },\n die: function( types, fn ) {\n jQuery( this.context ).off( types, this.selector || \"**\", fn );\n return this;\n },\n\n delegate: function( selector, types, data, fn ) {\n return this.on( types, selector, data, fn );\n },\n undelegate: function( selector, types, fn ) {\n // ( namespace ) or ( selector, types [, fn] )\n return arguments.length === 1 ? this.off( selector, \"**\" ) : this.off( types, selector || \"**\", fn );\n },\n\n trigger: function( type, data ) {\n return this.each(function() {\n jQuery.event.trigger( type, data, this );\n });\n },\n triggerHandler: function( type, data ) {\n if ( this[0] ) {\n return jQuery.event.trigger( type, data, this[0], true );\n }\n },\n\n toggle: function( fn ) {\n // Save reference to arguments for access in closure\n var args = arguments,\n guid = fn.guid || jQuery.guid++,\n i = 0,\n toggler = function( event ) {\n // Figure out which function to execute\n var lastToggle = ( jQuery._data( this, \"lastToggle\" + fn.guid ) || 0 ) % i;\n jQuery._data( this, \"lastToggle\" + fn.guid, lastToggle + 1 );\n\n // Make sure that clicks stop\n event.preventDefault();\n\n // and execute the function\n return args[ lastToggle ].apply( this, arguments ) || false;\n };\n\n // link all the functions, so any of them can unbind this click handler\n toggler.guid = guid;\n while ( i < args.length ) {\n args[ i++ ].guid = guid;\n }\n\n return this.click( toggler );\n },\n\n hover: function( fnOver, fnOut ) {\n return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n }\n});\n\njQuery.each( (\"blur focus focusin focusout load resize scroll unload click dblclick \" +\n \"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n \"change select submit keydown keypress keyup error contextmenu\").split(\" \"), function( i, name ) {\n\n // Handle event binding\n jQuery.fn[ name ] = function( data, fn ) {\n if ( fn == null ) {\n fn = data;\n data = null;\n }\n\n return arguments.length > 0 ?\n this.on( name, null, data, fn ) :\n this.trigger( name );\n };\n\n if ( rkeyEvent.test( name ) ) {\n jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;\n }\n\n if ( rmouseEvent.test( name ) ) {\n jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;\n }\n});\n/*!\n * Sizzle CSS Selector Engine\n * Copyright 2012 jQuery Foundation and other contributors\n * Released under the MIT license\n * http://sizzlejs.com/\n */\n(function( window, undefined ) {\n\nvar cachedruns,\n assertGetIdNotName,\n Expr,\n getText,\n isXML,\n contains,\n compile,\n sortOrder,\n hasDuplicate,\n outermostContext,\n\n baseHasDuplicate = true,\n strundefined = \"undefined\",\n\n expando = ( \"sizcache\" + Math.random() ).replace( \".\", \"\" ),\n\n Token = String,\n document = window.document,\n docElem = document.documentElement,\n dirruns = 0,\n done = 0,\n pop = [].pop,\n push = [].push,\n slice = [].slice,\n // Use a stripped-down indexOf if a native one is unavailable\n indexOf = [].indexOf || function( elem ) {\n var i = 0,\n len = this.length;\n for ( ; i < len; i++ ) {\n if ( this[i] === elem ) {\n return i;\n }\n }\n return -1;\n },\n\n // Augment a function for special use by Sizzle\n markFunction = function( fn, value ) {\n fn[ expando ] = value == null || value;\n return fn;\n },\n\n createCache = function() {\n var cache = {},\n keys = [];\n\n return markFunction(function( key, value ) {\n // Only keep the most recent entries\n if ( keys.push( key ) > Expr.cacheLength ) {\n delete cache[ keys.shift() ];\n }\n\n // Retrieve with (key + \" \") to avoid collision with native Object.prototype properties (see Issue #157)\n return (cache[ key + \" \" ] = value);\n }, cache );\n },\n\n classCache = createCache(),\n tokenCache = createCache(),\n compilerCache = createCache(),\n\n // Regex\n\n // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace\n whitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n // http://www.w3.org/TR/css3-syntax/#characters\n characterEncoding = \"(?:\\\\\\\\.|[-\\\\w]|[^\\\\x00-\\\\xa0])+\",\n\n // Loosely modeled on CSS identifier characters\n // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)\n // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier\n identifier = characterEncoding.replace( \"w\", \"w#\" ),\n\n // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors\n operators = \"([*^$|!~]?=)\",\n attributes = \"\\\\[\" + whitespace + \"*(\" + characterEncoding + \")\" + whitespace +\n \"*(?:\" + operators + whitespace + \"*(?:(['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|(\" + identifier + \")|)|)\" + whitespace + \"*\\\\]\",\n\n // Prefer arguments not in parens/brackets,\n // then attribute selectors and non-pseudos (denoted by :),\n // then anything else\n // These preferences are here to reduce the number of selectors\n // needing tokenize in the PSEUDO preFilter\n pseudos = \":(\" + characterEncoding + \")(?:\\\\((?:(['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\2|([^()[\\\\]]*|(?:(?:\" + attributes + \")|[^:]|\\\\\\\\.)*|.*))\\\\)|)\",\n\n // For matchExpr.POS and matchExpr.needsContext\n pos = \":(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\" + whitespace +\n \"*((?:-\\\\d)?\\\\d*)\" + whitespace + \"*\\\\)|)(?=[^-]|$)\",\n\n // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\n rtrim = new RegExp( \"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" + whitespace + \"+$\", \"g\" ),\n\n rcomma = new RegExp( \"^\" + whitespace + \"*,\" + whitespace + \"*\" ),\n rcombinators = new RegExp( \"^\" + whitespace + \"*([\\\\x20\\\\t\\\\r\\\\n\\\\f>+~])\" + whitespace + \"*\" ),\n rpseudo = new RegExp( pseudos ),\n\n // Easily-parseable/retrievable ID or TAG or CLASS selectors\n rquickExpr = /^(?:#([\\w\\-]+)|(\\w+)|\\.([\\w\\-]+))$/,\n\n rnot = /^:not/,\n rsibling = /[\\x20\\t\\r\\n\\f]*[+~]/,\n rendsWithNot = /:not\\($/,\n\n rheader = /h\\d/i,\n rinputs = /input|select|textarea|button/i,\n\n rbackslash = /\\\\(?!\\\\)/g,\n\n matchExpr = {\n \"ID\": new RegExp( \"^#(\" + characterEncoding + \")\" ),\n \"CLASS\": new RegExp( \"^\\\\.(\" + characterEncoding + \")\" ),\n \"NAME\": new RegExp( \"^\\\\[name=['\\\"]?(\" + characterEncoding + \")['\\\"]?\\\\]\" ),\n \"TAG\": new RegExp( \"^(\" + characterEncoding.replace( \"w\", \"w*\" ) + \")\" ),\n \"ATTR\": new RegExp( \"^\" + attributes ),\n \"PSEUDO\": new RegExp( \"^\" + pseudos ),\n \"POS\": new RegExp( pos, \"i\" ),\n \"CHILD\": new RegExp( \"^:(only|nth|first|last)-child(?:\\\\(\" + whitespace +\n \"*(even|odd|(([+-]|)(\\\\d*)n|)\" + whitespace + \"*(?:([+-]|)\" + whitespace +\n \"*(\\\\d+)|))\" + whitespace + \"*\\\\)|)\", \"i\" ),\n // For use in libraries implementing .is()\n \"needsContext\": new RegExp( \"^\" + whitespace + \"*[>+~]|\" + pos, \"i\" )\n },\n\n // Support\n\n // Used for testing something on an element\n assert = function( fn ) {\n var div = document.createElement(\"div\");\n\n try {\n return fn( div );\n } catch (e) {\n return false;\n } finally {\n // release memory in IE\n div = null;\n }\n },\n\n // Check if getElementsByTagName(\"*\") returns only elements\n assertTagNameNoComments = assert(function( div ) {\n div.appendChild( document.createComment(\"\") );\n return !div.getElementsByTagName(\"*\").length;\n }),\n\n // Check if getAttribute returns normalized href attributes\n assertHrefNotNormalized = assert(function( div ) {\n div.innerHTML = \"<a href='#'></a>\";\n return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&\n div.firstChild.getAttribute(\"href\") === \"#\";\n }),\n\n // Check if attributes should be retrieved by attribute nodes\n assertAttributes = assert(function( div ) {\n div.innerHTML = \"<select></select>\";\n var type = typeof div.lastChild.getAttribute(\"multiple\");\n // IE8 returns a string for some attributes even when not present\n return type !== \"boolean\" && type !== \"string\";\n }),\n\n // Check if getElementsByClassName can be trusted\n assertUsableClassName = assert(function( div ) {\n // Opera can't find a second classname (in 9.6)\n div.innerHTML = \"<div class='hidden e'></div><div class='hidden'></div>\";\n if ( !div.getElementsByClassName || !div.getElementsByClassName(\"e\").length ) {\n return false;\n }\n\n // Safari 3.2 caches class attributes and doesn't catch changes\n div.lastChild.className = \"e\";\n return div.getElementsByClassName(\"e\").length === 2;\n }),\n\n // Check if getElementById returns elements by name\n // Check if getElementsByName privileges form controls or returns elements by ID\n assertUsableName = assert(function( div ) {\n // Inject content\n div.id = expando + 0;\n div.innerHTML = \"<a name='\" + expando + \"'></a><div name='\" + expando + \"'></div>\";\n docElem.insertBefore( div, docElem.firstChild );\n\n // Test\n var pass = document.getElementsByName &&\n // buggy browsers will return fewer than the correct 2\n document.getElementsByName( expando ).length === 2 +\n // buggy browsers will return more than the correct 0\n document.getElementsByName( expando + 0 ).length;\n assertGetIdNotName = !document.getElementById( expando );\n\n // Cleanup\n docElem.removeChild( div );\n\n return pass;\n });\n\n// If slice is not available, provide a backup\ntry {\n slice.call( docElem.childNodes, 0 )[0].nodeType;\n} catch ( e ) {\n slice = function( i ) {\n var elem,\n results = [];\n for ( ; (elem = this[i]); i++ ) {\n results.push( elem );\n }\n return results;\n };\n}\n\nfunction Sizzle( selector, context, results, seed ) {\n results = results || [];\n context = context || document;\n var match, elem, xml, m,\n nodeType = context.nodeType;\n\n if ( !selector || typeof selector !== \"string\" ) {\n return results;\n }\n\n if ( nodeType !== 1 && nodeType !== 9 ) {\n return [];\n }\n\n xml = isXML( context );\n\n if ( !xml && !seed ) {\n if ( (match = rquickExpr.exec( selector )) ) {\n // Speed-up: Sizzle(\"#ID\")\n if ( (m = match[1]) ) {\n if ( nodeType === 9 ) {\n elem = context.getElementById( m );\n // Check parentNode to catch when Blackberry 4.6 returns\n // nodes that are no longer in the document #6963\n if ( elem && elem.parentNode ) {\n // Handle the case where IE, Opera, and Webkit return items\n // by name instead of ID\n if ( elem.id === m ) {\n results.push( elem );\n return results;\n }\n } else {\n return results;\n }\n } else {\n // Context is not a document\n if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&\n contains( context, elem ) && elem.id === m ) {\n results.push( elem );\n return results;\n }\n }\n\n // Speed-up: Sizzle(\"TAG\")\n } else if ( match[2] ) {\n push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );\n return results;\n\n // Speed-up: Sizzle(\".CLASS\")\n } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {\n push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );\n return results;\n }\n }\n }\n\n // All others\n return select( selector.replace( rtrim, \"$1\" ), context, results, seed, xml );\n}\n\nSizzle.matches = function( expr, elements ) {\n return Sizzle( expr, null, null, elements );\n};\n\nSizzle.matchesSelector = function( elem, expr ) {\n return Sizzle( expr, null, null, [ elem ] ).length > 0;\n};\n\n// Returns a function to use in pseudos for input types\nfunction createInputPseudo( type ) {\n return function( elem ) {\n var name = elem.nodeName.toLowerCase();\n return name === \"input\" && elem.type === type;\n };\n}\n\n// Returns a function to use in pseudos for buttons\nfunction createButtonPseudo( type ) {\n return function( elem ) {\n var name = elem.nodeName.toLowerCase();\n return (name === \"input\" || name === \"button\") && elem.type === type;\n };\n}\n\n// Returns a function to use in pseudos for positionals\nfunction createPositionalPseudo( fn ) {\n return markFunction(function( argument ) {\n argument = +argument;\n return markFunction(function( seed, matches ) {\n var j,\n matchIndexes = fn( [], seed.length, argument ),\n i = matchIndexes.length;\n\n // Match elements found at the specified indexes\n while ( i-- ) {\n if ( seed[ (j = matchIndexes[i]) ] ) {\n seed[j] = !(matches[j] = seed[j]);\n }\n }\n });\n });\n}\n\n/**\n * Utility function for retrieving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\ngetText = Sizzle.getText = function( elem ) {\n var node,\n ret = \"\",\n i = 0,\n nodeType = elem.nodeType;\n\n if ( nodeType ) {\n if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\n // Use textContent for elements\n // innerText usage removed for consistency of new lines (see #11153)\n if ( typeof elem.textContent === \"string\" ) {\n return elem.textContent;\n } else {\n // Traverse its children\n for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n ret += getText( elem );\n }\n }\n } else if ( nodeType === 3 || nodeType === 4 ) {\n return elem.nodeValue;\n }\n // Do not include comment or processing instruction nodes\n } else {\n\n // If no nodeType, this is expected to be an array\n for ( ; (node = elem[i]); i++ ) {\n // Do not traverse comment nodes\n ret += getText( node );\n }\n }\n return ret;\n};\n\nisXML = Sizzle.isXML = function( elem ) {\n // documentElement is verified for cases where it doesn't yet exist\n // (such as loading iframes in IE - #4833)\n var documentElement = elem && (elem.ownerDocument || elem).documentElement;\n return documentElement ? documentElement.nodeName !== \"HTML\" : false;\n};\n\n// Element contains another\ncontains = Sizzle.contains = docElem.contains ?\n function( a, b ) {\n var adown = a.nodeType === 9 ? a.documentElement : a,\n bup = b && b.parentNode;\n return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );\n } :\n docElem.compareDocumentPosition ?\n function( a, b ) {\n return b && !!( a.compareDocumentPosition( b ) & 16 );\n } :\n function( a, b ) {\n while ( (b = b.parentNode) ) {\n if ( b === a ) {\n return true;\n }\n }\n return false;\n };\n\nSizzle.attr = function( elem, name ) {\n var val,\n xml = isXML( elem );\n\n if ( !xml ) {\n name = name.toLowerCase();\n }\n if ( (val = Expr.attrHandle[ name ]) ) {\n return val( elem );\n }\n if ( xml || assertAttributes ) {\n return elem.getAttribute( name );\n }\n val = elem.getAttributeNode( name );\n return val ?\n typeof elem[ name ] === \"boolean\" ?\n elem[ name ] ? name : null :\n val.specified ? val.value : null :\n null;\n};\n\nExpr = Sizzle.selectors = {\n\n // Can be adjusted by the user\n cacheLength: 50,\n\n createPseudo: markFunction,\n\n match: matchExpr,\n\n // IE6/7 return a modified href\n attrHandle: assertHrefNotNormalized ?\n {} :\n {\n \"href\": function( elem ) {\n return elem.getAttribute( \"href\", 2 );\n },\n \"type\": function( elem ) {\n return elem.getAttribute(\"type\");\n }\n },\n\n find: {\n \"ID\": assertGetIdNotName ?\n function( id, context, xml ) {\n if ( typeof context.getElementById !== strundefined && !xml ) {\n var m = context.getElementById( id );\n // Check parentNode to catch when Blackberry 4.6 returns\n // nodes that are no longer in the document #6963\n return m && m.parentNode ? [m] : [];\n }\n } :\n function( id, context, xml ) {\n if ( typeof context.getElementById !== strundefined && !xml ) {\n var m = context.getElementById( id );\n\n return m ?\n m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode(\"id\").value === id ?\n [m] :\n undefined :\n [];\n }\n },\n\n \"TAG\": assertTagNameNoComments ?\n function( tag, context ) {\n if ( typeof context.getElementsByTagName !== strundefined ) {\n return context.getElementsByTagName( tag );\n }\n } :\n function( tag, context ) {\n var results = context.getElementsByTagName( tag );\n\n // Filter out possible comments\n if ( tag === \"*\" ) {\n var elem,\n tmp = [],\n i = 0;\n\n for ( ; (elem = results[i]); i++ ) {\n if ( elem.nodeType === 1 ) {\n tmp.push( elem );\n }\n }\n\n return tmp;\n }\n return results;\n },\n\n \"NAME\": assertUsableName && function( tag, context ) {\n if ( typeof context.getElementsByName !== strundefined ) {\n return context.getElementsByName( name );\n }\n },\n\n \"CLASS\": assertUsableClassName && function( className, context, xml ) {\n if ( typeof context.getElementsByClassName !== strundefined && !xml ) {\n return context.getElementsByClassName( className );\n }\n }\n },\n\n relative: {\n \">\": { dir: \"parentNode\", first: true },\n \" \": { dir: \"parentNode\" },\n \"+\": { dir: \"previousSibling\", first: true },\n \"~\": { dir: \"previousSibling\" }\n },\n\n preFilter: {\n \"ATTR\": function( match ) {\n match[1] = match[1].replace( rbackslash, \"\" );\n\n // Move the given value to match[3] whether quoted or unquoted\n match[3] = ( match[4] || match[5] || \"\" ).replace( rbackslash, \"\" );\n\n if ( match[2] === \"~=\" ) {\n match[3] = \" \" + match[3] + \" \";\n }\n\n return match.slice( 0, 4 );\n },\n\n \"CHILD\": function( match ) {\n /* matches from matchExpr[\"CHILD\"]\n 1 type (only|nth|...)\n 2 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n 3 xn-component of xn+y argument ([+-]?\\d*n|)\n 4 sign of xn-component\n 5 x of xn-component\n 6 sign of y-component\n 7 y of y-component\n */\n match[1] = match[1].toLowerCase();\n\n if ( match[1] === \"nth\" ) {\n // nth-child requires argument\n if ( !match[2] ) {\n Sizzle.error( match[0] );\n }\n\n // numeric x and y parameters for Expr.filter.CHILD\n // remember that false/true cast respectively to 0/1\n match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === \"even\" || match[2] === \"odd\" ) );\n match[4] = +( ( match[6] + match[7] ) || match[2] === \"odd\" );\n\n // other types prohibit arguments\n } else if ( match[2] ) {\n Sizzle.error( match[0] );\n }\n\n return match;\n },\n\n \"PSEUDO\": function( match ) {\n var unquoted, excess;\n if ( matchExpr[\"CHILD\"].test( match[0] ) ) {\n return null;\n }\n\n if ( match[3] ) {\n match[2] = match[3];\n } else if ( (unquoted = match[4]) ) {\n // Only check arguments that contain a pseudo\n if ( rpseudo.test(unquoted) &&\n // Get excess from tokenize (recursively)\n (excess = tokenize( unquoted, true )) &&\n // advance to the next closing parenthesis\n (excess = unquoted.indexOf( \")\", unquoted.length - excess ) - unquoted.length) ) {\n\n // excess is a negative index\n unquoted = unquoted.slice( 0, excess );\n match[0] = match[0].slice( 0, excess );\n }\n match[2] = unquoted;\n }\n\n // Return only captures needed by the pseudo filter method (type and argument)\n return match.slice( 0, 3 );\n }\n },\n\n filter: {\n \"ID\": assertGetIdNotName ?\n function( id ) {\n id = id.replace( rbackslash, \"\" );\n return function( elem ) {\n return elem.getAttribute(\"id\") === id;\n };\n } :\n function( id ) {\n id = id.replace( rbackslash, \"\" );\n return function( elem ) {\n var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode(\"id\");\n return node && node.value === id;\n };\n },\n\n \"TAG\": function( nodeName ) {\n if ( nodeName === \"*\" ) {\n return function() { return true; };\n }\n nodeName = nodeName.replace( rbackslash, \"\" ).toLowerCase();\n\n return function( elem ) {\n return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n };\n },\n\n \"CLASS\": function( className ) {\n var pattern = classCache[ expando ][ className + \" \" ];\n\n return pattern ||\n (pattern = new RegExp( \"(^|\" + whitespace + \")\" + className + \"(\" + whitespace + \"|$)\" )) &&\n classCache( className, function( elem ) {\n return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute(\"class\")) || \"\" );\n });\n },\n\n \"ATTR\": function( name, operator, check ) {\n return function( elem, context ) {\n var result = Sizzle.attr( elem, name );\n\n if ( result == null ) {\n return operator === \"!=\";\n }\n if ( !operator ) {\n return true;\n }\n\n result += \"\";\n\n return operator === \"=\" ? result === check :\n operator === \"!=\" ? result !== check :\n operator === \"^=\" ? check && result.indexOf( check ) === 0 :\n operator === \"*=\" ? check && result.indexOf( check ) > -1 :\n operator === \"$=\" ? check && result.substr( result.length - check.length ) === check :\n operator === \"~=\" ? ( \" \" + result + \" \" ).indexOf( check ) > -1 :\n operator === \"|=\" ? result === check || result.substr( 0, check.length + 1 ) === check + \"-\" :\n false;\n };\n },\n\n \"CHILD\": function( type, argument, first, last ) {\n\n if ( type === \"nth\" ) {\n return function( elem ) {\n var node, diff,\n parent = elem.parentNode;\n\n if ( first === 1 && last === 0 ) {\n return true;\n }\n\n if ( parent ) {\n diff = 0;\n for ( node = parent.firstChild; node; node = node.nextSibling ) {\n if ( node.nodeType === 1 ) {\n diff++;\n if ( elem === node ) {\n break;\n }\n }\n }\n }\n\n // Incorporate the offset (or cast to NaN), then check against cycle size\n diff -= last;\n return diff === first || ( diff % first === 0 && diff / first >= 0 );\n };\n }\n\n return function( elem ) {\n var node = elem;\n\n switch ( type ) {\n case \"only\":\n case \"first\":\n while ( (node = node.previousSibling) ) {\n if ( node.nodeType === 1 ) {\n return false;\n }\n }\n\n if ( type === \"first\" ) {\n return true;\n }\n\n node = elem;\n\n /* falls through */\n case \"last\":\n while ( (node = node.nextSibling) ) {\n if ( node.nodeType === 1 ) {\n return false;\n }\n }\n\n return true;\n }\n };\n },\n\n \"PSEUDO\": function( pseudo, argument ) {\n // pseudo-class names are case-insensitive\n // http://www.w3.org/TR/selectors/#pseudo-classes\n // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\n // Remember that setFilters inherits from pseudos\n var args,\n fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||\n Sizzle.error( \"unsupported pseudo: \" + pseudo );\n\n // The user may use createPseudo to indicate that\n // arguments are needed to create the filter function\n // just as Sizzle does\n if ( fn[ expando ] ) {\n return fn( argument );\n }\n\n // But maintain support for old signatures\n if ( fn.length > 1 ) {\n args = [ pseudo, pseudo, \"\", argument ];\n return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?\n markFunction(function( seed, matches ) {\n var idx,\n matched = fn( seed, argument ),\n i = matched.length;\n while ( i-- ) {\n idx = indexOf.call( seed, matched[i] );\n seed[ idx ] = !( matches[ idx ] = matched[i] );\n }\n }) :\n function( elem ) {\n return fn( elem, 0, args );\n };\n }\n\n return fn;\n }\n },\n\n pseudos: {\n \"not\": markFunction(function( selector ) {\n // Trim the selector passed to compile\n // to avoid treating leading and trailing\n // spaces as combinators\n var input = [],\n results = [],\n matcher = compile( selector.replace( rtrim, \"$1\" ) );\n\n return matcher[ expando ] ?\n markFunction(function( seed, matches, context, xml ) {\n var elem,\n unmatched = matcher( seed, null, xml, [] ),\n i = seed.length;\n\n // Match elements unmatched by `matcher`\n while ( i-- ) {\n if ( (elem = unmatched[i]) ) {\n seed[i] = !(matches[i] = elem);\n }\n }\n }) :\n function( elem, context, xml ) {\n input[0] = elem;\n matcher( input, null, xml, results );\n return !results.pop();\n };\n }),\n\n \"has\": markFunction(function( selector ) {\n return function( elem ) {\n return Sizzle( selector, elem ).length > 0;\n };\n }),\n\n \"contains\": markFunction(function( text ) {\n return function( elem ) {\n return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;\n };\n }),\n\n \"enabled\": function( elem ) {\n return elem.disabled === false;\n },\n\n \"disabled\": function( elem ) {\n return elem.disabled === true;\n },\n\n \"checked\": function( elem ) {\n // In CSS3, :checked should return both checked and selected elements\n // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n var nodeName = elem.nodeName.toLowerCase();\n return (nodeName === \"input\" && !!elem.checked) || (nodeName === \"option\" && !!elem.selected);\n },\n\n \"selected\": function( elem ) {\n // Accessing this property makes selected-by-default\n // options in Safari work properly\n if ( elem.parentNode ) {\n elem.parentNode.selectedIndex;\n }\n\n return elem.selected === true;\n },\n\n \"parent\": function( elem ) {\n return !Expr.pseudos[\"empty\"]( elem );\n },\n\n \"empty\": function( elem ) {\n // http://www.w3.org/TR/selectors/#empty-pseudo\n // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),\n // not comment, processing instructions, or others\n // Thanks to Diego Perini for the nodeName shortcut\n // Greater than \"@\" means alpha characters (specifically not starting with \"#\" or \"?\")\n var nodeType;\n elem = elem.firstChild;\n while ( elem ) {\n if ( elem.nodeName > \"@\" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {\n return false;\n }\n elem = elem.nextSibling;\n }\n return true;\n },\n\n \"header\": function( elem ) {\n return rheader.test( elem.nodeName );\n },\n\n \"text\": function( elem ) {\n var type, attr;\n // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)\n // use getAttribute instead to test this case\n return elem.nodeName.toLowerCase() === \"input\" &&\n (type = elem.type) === \"text\" &&\n ( (attr = elem.getAttribute(\"type\")) == null || attr.toLowerCase() === type );\n },\n\n // Input types\n \"radio\": createInputPseudo(\"radio\"),\n \"checkbox\": createInputPseudo(\"checkbox\"),\n \"file\": createInputPseudo(\"file\"),\n \"password\": createInputPseudo(\"password\"),\n \"image\": createInputPseudo(\"image\"),\n\n \"submit\": createButtonPseudo(\"submit\"),\n \"reset\": createButtonPseudo(\"reset\"),\n\n \"button\": function( elem ) {\n var name = elem.nodeName.toLowerCase();\n return name === \"input\" && elem.type === \"button\" || name === \"button\";\n },\n\n \"input\": function( elem ) {\n return rinputs.test( elem.nodeName );\n },\n\n \"focus\": function( elem ) {\n var doc = elem.ownerDocument;\n return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);\n },\n\n \"active\": function( elem ) {\n return elem === elem.ownerDocument.activeElement;\n },\n\n // Positional types\n \"first\": createPositionalPseudo(function() {\n return [ 0 ];\n }),\n\n \"last\": createPositionalPseudo(function( matchIndexes, length ) {\n return [ length - 1 ];\n }),\n\n \"eq\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n return [ argument < 0 ? argument + length : argument ];\n }),\n\n \"even\": createPositionalPseudo(function( matchIndexes, length ) {\n for ( var i = 0; i < length; i += 2 ) {\n matchIndexes.push( i );\n }\n return matchIndexes;\n }),\n\n \"odd\": createPositionalPseudo(function( matchIndexes, length ) {\n for ( var i = 1; i < length; i += 2 ) {\n matchIndexes.push( i );\n }\n return matchIndexes;\n }),\n\n \"lt\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {\n matchIndexes.push( i );\n }\n return matchIndexes;\n }),\n\n \"gt\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {\n matchIndexes.push( i );\n }\n return matchIndexes;\n })\n }\n};\n\nfunction siblingCheck( a, b, ret ) {\n if ( a === b ) {\n return ret;\n }\n\n var cur = a.nextSibling;\n\n while ( cur ) {\n if ( cur === b ) {\n return -1;\n }\n\n cur = cur.nextSibling;\n }\n\n return 1;\n}\n\nsortOrder = docElem.compareDocumentPosition ?\n function( a, b ) {\n if ( a === b ) {\n hasDuplicate = true;\n return 0;\n }\n\n return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?\n a.compareDocumentPosition :\n a.compareDocumentPosition(b) & 4\n ) ? -1 : 1;\n } :\n function( a, b ) {\n // The nodes are identical, we can exit early\n if ( a === b ) {\n hasDuplicate = true;\n return 0;\n\n // Fallback to using sourceIndex (in IE) if it's available on both nodes\n } else if ( a.sourceIndex && b.sourceIndex ) {\n return a.sourceIndex - b.sourceIndex;\n }\n\n var al, bl,\n ap = [],\n bp = [],\n aup = a.parentNode,\n bup = b.parentNode,\n cur = aup;\n\n // If the nodes are siblings (or identical) we can do a quick check\n if ( aup === bup ) {\n return siblingCheck( a, b );\n\n // If no parents were found then the nodes are disconnected\n } else if ( !aup ) {\n return -1;\n\n } else if ( !bup ) {\n return 1;\n }\n\n // Otherwise they're somewhere else in the tree so we need\n // to build up a full list of the parentNodes for comparison\n while ( cur ) {\n ap.unshift( cur );\n cur = cur.parentNode;\n }\n\n cur = bup;\n\n while ( cur ) {\n bp.unshift( cur );\n cur = cur.parentNode;\n }\n\n al = ap.length;\n bl = bp.length;\n\n // Start walking down the tree looking for a discrepancy\n for ( var i = 0; i < al && i < bl; i++ ) {\n if ( ap[i] !== bp[i] ) {\n return siblingCheck( ap[i], bp[i] );\n }\n }\n\n // We ended someplace up the tree so do a sibling check\n return i === al ?\n siblingCheck( a, bp[i], -1 ) :\n siblingCheck( ap[i], b, 1 );\n };\n\n// Always assume the presence of duplicates if sort doesn't\n// pass them to our comparison function (as in Google Chrome).\n[0, 0].sort( sortOrder );\nbaseHasDuplicate = !hasDuplicate;\n\n// Document sorting and removing duplicates\nSizzle.uniqueSort = function( results ) {\n var elem,\n duplicates = [],\n i = 1,\n j = 0;\n\n hasDuplicate = baseHasDuplicate;\n results.sort( sortOrder );\n\n if ( hasDuplicate ) {\n for ( ; (elem = results[i]); i++ ) {\n if ( elem === results[ i - 1 ] ) {\n j = duplicates.push( i );\n }\n }\n while ( j-- ) {\n results.splice( duplicates[ j ], 1 );\n }\n }\n\n return results;\n};\n\nSizzle.error = function( msg ) {\n throw new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\nfunction tokenize( selector, parseOnly ) {\n var matched, match, tokens, type,\n soFar, groups, preFilters,\n cached = tokenCache[ expando ][ selector + \" \" ];\n\n if ( cached ) {\n return parseOnly ? 0 : cached.slice( 0 );\n }\n\n soFar = selector;\n groups = [];\n preFilters = Expr.preFilter;\n\n while ( soFar ) {\n\n // Comma and first run\n if ( !matched || (match = rcomma.exec( soFar )) ) {\n if ( match ) {\n // Don't consume trailing commas as valid\n soFar = soFar.slice( match[0].length ) || soFar;\n }\n groups.push( tokens = [] );\n }\n\n matched = false;\n\n // Combinators\n if ( (match = rcombinators.exec( soFar )) ) {\n tokens.push( matched = new Token( match.shift() ) );\n soFar = soFar.slice( matched.length );\n\n // Cast descendant combinators to space\n matched.type = match[0].replace( rtrim, \" \" );\n }\n\n // Filters\n for ( type in Expr.filter ) {\n if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||\n (match = preFilters[ type ]( match ))) ) {\n\n tokens.push( matched = new Token( match.shift() ) );\n soFar = soFar.slice( matched.length );\n matched.type = type;\n matched.matches = match;\n }\n }\n\n if ( !matched ) {\n break;\n }\n }\n\n // Return the length of the invalid excess\n // if we're just parsing\n // Otherwise, throw an error or return tokens\n return parseOnly ?\n soFar.length :\n soFar ?\n Sizzle.error( selector ) :\n // Cache the tokens\n tokenCache( selector, groups ).slice( 0 );\n}\n\nfunction addCombinator( matcher, combinator, base ) {\n var dir = combinator.dir,\n checkNonElements = base && combinator.dir === \"parentNode\",\n doneName = done++;\n\n return combinator.first ?\n // Check against closest ancestor/preceding element\n function( elem, context, xml ) {\n while ( (elem = elem[ dir ]) ) {\n if ( checkNonElements || elem.nodeType === 1 ) {\n return matcher( elem, context, xml );\n }\n }\n } :\n\n // Check against all ancestor/preceding elements\n function( elem, context, xml ) {\n // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching\n if ( !xml ) {\n var cache,\n dirkey = dirruns + \" \" + doneName + \" \",\n cachedkey = dirkey + cachedruns;\n while ( (elem = elem[ dir ]) ) {\n if ( checkNonElements || elem.nodeType === 1 ) {\n if ( (cache = elem[ expando ]) === cachedkey ) {\n return elem.sizset;\n } else if ( typeof cache === \"string\" && cache.indexOf(dirkey) === 0 ) {\n if ( elem.sizset ) {\n return elem;\n }\n } else {\n elem[ expando ] = cachedkey;\n if ( matcher( elem, context, xml ) ) {\n elem.sizset = true;\n return elem;\n }\n elem.sizset = false;\n }\n }\n }\n } else {\n while ( (elem = elem[ dir ]) ) {\n if ( checkNonElements || elem.nodeType === 1 ) {\n if ( matcher( elem, context, xml ) ) {\n return elem;\n }\n }\n }\n }\n };\n}\n\nfunction elementMatcher( matchers ) {\n return matchers.length > 1 ?\n function( elem, context, xml ) {\n var i = matchers.length;\n while ( i-- ) {\n if ( !matchers[i]( elem, context, xml ) ) {\n return false;\n }\n }\n return true;\n } :\n matchers[0];\n}\n\nfunction condense( unmatched, map, filter, context, xml ) {\n var elem,\n newUnmatched = [],\n i = 0,\n len = unmatched.length,\n mapped = map != null;\n\n for ( ; i < len; i++ ) {\n if ( (elem = unmatched[i]) ) {\n if ( !filter || filter( elem, context, xml ) ) {\n newUnmatched.push( elem );\n if ( mapped ) {\n map.push( i );\n }\n }\n }\n }\n\n return newUnmatched;\n}\n\nfunction setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {\n if ( postFilter && !postFilter[ expando ] ) {\n postFilter = setMatcher( postFilter );\n }\n if ( postFinder && !postFinder[ expando ] ) {\n postFinder = setMatcher( postFinder, postSelector );\n }\n return markFunction(function( seed, results, context, xml ) {\n var temp, i, elem,\n preMap = [],\n postMap = [],\n preexisting = results.length,\n\n // Get initial elements from seed or context\n elems = seed || multipleContexts( selector || \"*\", context.nodeType ? [ context ] : context, [] ),\n\n // Prefilter to get matcher input, preserving a map for seed-results synchronization\n matcherIn = preFilter && ( seed || !selector ) ?\n condense( elems, preMap, preFilter, context, xml ) :\n elems,\n\n matcherOut = matcher ?\n // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,\n postFinder || ( seed ? preFilter : preexisting || postFilter ) ?\n\n // ...intermediate processing is necessary\n [] :\n\n // ...otherwise use results directly\n results :\n matcherIn;\n\n // Find primary matches\n if ( matcher ) {\n matcher( matcherIn, matcherOut, context, xml );\n }\n\n // Apply postFilter\n if ( postFilter ) {\n temp = condense( matcherOut, postMap );\n postFilter( temp, [], context, xml );\n\n // Un-match failing elements by moving them back to matcherIn\n i = temp.length;\n while ( i-- ) {\n if ( (elem = temp[i]) ) {\n matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);\n }\n }\n }\n\n if ( seed ) {\n if ( postFinder || preFilter ) {\n if ( postFinder ) {\n // Get the final matcherOut by condensing this intermediate into postFinder contexts\n temp = [];\n i = matcherOut.length;\n while ( i-- ) {\n if ( (elem = matcherOut[i]) ) {\n // Restore matcherIn since elem is not yet a final match\n temp.push( (matcherIn[i] = elem) );\n }\n }\n postFinder( null, (matcherOut = []), temp, xml );\n }\n\n // Move matched elements from seed to results to keep them synchronized\n i = matcherOut.length;\n while ( i-- ) {\n if ( (elem = matcherOut[i]) &&\n (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {\n\n seed[temp] = !(results[temp] = elem);\n }\n }\n }\n\n // Add elements to results, through postFinder if defined\n } else {\n matcherOut = condense(\n matcherOut === results ?\n matcherOut.splice( preexisting, matcherOut.length ) :\n matcherOut\n );\n if ( postFinder ) {\n postFinder( null, results, matcherOut, xml );\n } else {\n push.apply( results, matcherOut );\n }\n }\n });\n}\n\nfunction matcherFromTokens( tokens ) {\n var checkContext, matcher, j,\n len = tokens.length,\n leadingRelative = Expr.relative[ tokens[0].type ],\n implicitRelative = leadingRelative || Expr.relative[\" \"],\n i = leadingRelative ? 1 : 0,\n\n // The foundational matcher ensures that elements are reachable from top-level context(s)\n matchContext = addCombinator( function( elem ) {\n return elem === checkContext;\n }, implicitRelative, true ),\n matchAnyContext = addCombinator( function( elem ) {\n return indexOf.call( checkContext, elem ) > -1;\n }, implicitRelative, true ),\n matchers = [ function( elem, context, xml ) {\n return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (\n (checkContext = context).nodeType ?\n matchContext( elem, context, xml ) :\n matchAnyContext( elem, context, xml ) );\n } ];\n\n for ( ; i < len; i++ ) {\n if ( (matcher = Expr.relative[ tokens[i].type ]) ) {\n matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];\n } else {\n matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );\n\n // Return special upon seeing a positional matcher\n if ( matcher[ expando ] ) {\n // Find the next relative operator (if any) for proper handling\n j = ++i;\n for ( ; j < len; j++ ) {\n if ( Expr.relative[ tokens[j].type ] ) {\n break;\n }\n }\n return setMatcher(\n i > 1 && elementMatcher( matchers ),\n i > 1 && tokens.slice( 0, i - 1 ).join(\"\").replace( rtrim, \"$1\" ),\n matcher,\n i < j && matcherFromTokens( tokens.slice( i, j ) ),\n j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),\n j < len && tokens.join(\"\")\n );\n }\n matchers.push( matcher );\n }\n }\n\n return elementMatcher( matchers );\n}\n\nfunction matcherFromGroupMatchers( elementMatchers, setMatchers ) {\n var bySet = setMatchers.length > 0,\n byElement = elementMatchers.length > 0,\n superMatcher = function( seed, context, xml, results, expandContext ) {\n var elem, j, matcher,\n setMatched = [],\n matchedCount = 0,\n i = \"0\",\n unmatched = seed && [],\n outermost = expandContext != null,\n contextBackup = outermostContext,\n // We must always have either seed elements or context\n elems = seed || byElement && Expr.find[\"TAG\"]( \"*\", expandContext && context.parentNode || context ),\n // Nested matchers should use non-integer dirruns\n dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);\n\n if ( outermost ) {\n outermostContext = context !== document && context;\n cachedruns = superMatcher.el;\n }\n\n // Add elements passing elementMatchers directly to results\n for ( ; (elem = elems[i]) != null; i++ ) {\n if ( byElement && elem ) {\n for ( j = 0; (matcher = elementMatchers[j]); j++ ) {\n if ( matcher( elem, context, xml ) ) {\n results.push( elem );\n break;\n }\n }\n if ( outermost ) {\n dirruns = dirrunsUnique;\n cachedruns = ++superMatcher.el;\n }\n }\n\n // Track unmatched elements for set filters\n if ( bySet ) {\n // They will have gone through all possible matchers\n if ( (elem = !matcher && elem) ) {\n matchedCount--;\n }\n\n // Lengthen the array for every element, matched or not\n if ( seed ) {\n unmatched.push( elem );\n }\n }\n }\n\n // Apply set filters to unmatched elements\n matchedCount += i;\n if ( bySet && i !== matchedCount ) {\n for ( j = 0; (matcher = setMatchers[j]); j++ ) {\n matcher( unmatched, setMatched, context, xml );\n }\n\n if ( seed ) {\n // Reintegrate element matches to eliminate the need for sorting\n if ( matchedCount > 0 ) {\n while ( i-- ) {\n if ( !(unmatched[i] || setMatched[i]) ) {\n setMatched[i] = pop.call( results );\n }\n }\n }\n\n // Discard index placeholder values to get only actual matches\n setMatched = condense( setMatched );\n }\n\n // Add matches to results\n push.apply( results, setMatched );\n\n // Seedless set matches succeeding multiple successful matchers stipulate sorting\n if ( outermost && !seed && setMatched.length > 0 &&\n ( matchedCount + setMatchers.length ) > 1 ) {\n\n Sizzle.uniqueSort( results );\n }\n }\n\n // Override manipulation of globals by nested matchers\n if ( outermost ) {\n dirruns = dirrunsUnique;\n outermostContext = contextBackup;\n }\n\n return unmatched;\n };\n\n superMatcher.el = 0;\n return bySet ?\n markFunction( superMatcher ) :\n superMatcher;\n}\n\ncompile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {\n var i,\n setMatchers = [],\n elementMatchers = [],\n cached = compilerCache[ expando ][ selector + \" \" ];\n\n if ( !cached ) {\n // Generate a function of recursive functions that can be used to check each element\n if ( !group ) {\n group = tokenize( selector );\n }\n i = group.length;\n while ( i-- ) {\n cached = matcherFromTokens( group[i] );\n if ( cached[ expando ] ) {\n setMatchers.push( cached );\n } else {\n elementMatchers.push( cached );\n }\n }\n\n // Cache the compiled function\n cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );\n }\n return cached;\n};\n\nfunction multipleContexts( selector, contexts, results ) {\n var i = 0,\n len = contexts.length;\n for ( ; i < len; i++ ) {\n Sizzle( selector, contexts[i], results );\n }\n return results;\n}\n\nfunction select( selector, context, results, seed, xml ) {\n var i, tokens, token, type, find,\n match = tokenize( selector ),\n j = match.length;\n\n if ( !seed ) {\n // Try to minimize operations if there is only one group\n if ( match.length === 1 ) {\n\n // Take a shortcut and set the context if the root selector is an ID\n tokens = match[0] = match[0].slice( 0 );\n if ( tokens.length > 2 && (token = tokens[0]).type === \"ID\" &&\n context.nodeType === 9 && !xml &&\n Expr.relative[ tokens[1].type ] ) {\n\n context = Expr.find[\"ID\"]( token.matches[0].replace( rbackslash, \"\" ), context, xml )[0];\n if ( !context ) {\n return results;\n }\n\n selector = selector.slice( tokens.shift().length );\n }\n\n // Fetch a seed set for right-to-left matching\n for ( i = matchExpr[\"POS\"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {\n token = tokens[i];\n\n // Abort if we hit a combinator\n if ( Expr.relative[ (type = token.type) ] ) {\n break;\n }\n if ( (find = Expr.find[ type ]) ) {\n // Search, expanding context for leading sibling combinators\n if ( (seed = find(\n token.matches[0].replace( rbackslash, \"\" ),\n rsibling.test( tokens[0].type ) && context.parentNode || context,\n xml\n )) ) {\n\n // If seed is empty or no tokens remain, we can return early\n tokens.splice( i, 1 );\n selector = seed.length && tokens.join(\"\");\n if ( !selector ) {\n push.apply( results, slice.call( seed, 0 ) );\n return results;\n }\n\n break;\n }\n }\n }\n }\n }\n\n // Compile and execute a filtering function\n // Provide `match` to avoid retokenization if we modified the selector above\n compile( selector, match )(\n seed,\n context,\n xml,\n results,\n rsibling.test( selector )\n );\n return results;\n}\n\nif ( document.querySelectorAll ) {\n (function() {\n var disconnectedMatch,\n oldSelect = select,\n rescape = /'|\\\\/g,\n rattributeQuotes = /\\=[\\x20\\t\\r\\n\\f]*([^'\"\\]]*)[\\x20\\t\\r\\n\\f]*\\]/g,\n\n // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA\n // A support test would require too much code (would include document ready)\n rbuggyQSA = [ \":focus\" ],\n\n // matchesSelector(:active) reports false when true (IE9/Opera 11.5)\n // A support test would require too much code (would include document ready)\n // just skip matchesSelector for :active\n rbuggyMatches = [ \":active\" ],\n matches = docElem.matchesSelector ||\n docElem.mozMatchesSelector ||\n docElem.webkitMatchesSelector ||\n docElem.oMatchesSelector ||\n docElem.msMatchesSelector;\n\n // Build QSA regex\n // Regex strategy adopted from Diego Perini\n assert(function( div ) {\n // Select is set to empty string on purpose\n // This is to test IE's treatment of not explictly\n // setting a boolean content attribute,\n // since its presence should be enough\n // http://bugs.jquery.com/ticket/12359\n div.innerHTML = \"<select><option selected=''></option></select>\";\n\n // IE8 - Some boolean attributes are not treated correctly\n if ( !div.querySelectorAll(\"[selected]\").length ) {\n rbuggyQSA.push( \"\\\\[\" + whitespace + \"*(?:checked|disabled|ismap|multiple|readonly|selected|value)\" );\n }\n\n // Webkit/Opera - :checked should return selected option elements\n // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n // IE8 throws error here (do not put tests after this one)\n if ( !div.querySelectorAll(\":checked\").length ) {\n rbuggyQSA.push(\":checked\");\n }\n });\n\n assert(function( div ) {\n\n // Opera 10-12/IE9 - ^= $= *= and empty values\n // Should not select anything\n div.innerHTML = \"<p test=''></p>\";\n if ( div.querySelectorAll(\"[test^='']\").length ) {\n rbuggyQSA.push( \"[*^$]=\" + whitespace + \"*(?:\\\"\\\"|'')\" );\n }\n\n // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\n // IE8 throws error here (do not put tests after this one)\n div.innerHTML = \"<input type='hidden'/>\";\n if ( !div.querySelectorAll(\":enabled\").length ) {\n rbuggyQSA.push(\":enabled\", \":disabled\");\n }\n });\n\n // rbuggyQSA always contains :focus, so no need for a length check\n rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join(\"|\") );\n\n select = function( selector, context, results, seed, xml ) {\n // Only use querySelectorAll when not filtering,\n // when this is not xml,\n // and when no QSA bugs apply\n if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {\n var groups, i,\n old = true,\n nid = expando,\n newContext = context,\n newSelector = context.nodeType === 9 && selector;\n\n // qSA works strangely on Element-rooted queries\n // We can work around this by specifying an extra ID on the root\n // and working up from there (Thanks to Andrew Dupont for the technique)\n // IE 8 doesn't work on object elements\n if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== \"object\" ) {\n groups = tokenize( selector );\n\n if ( (old = context.getAttribute(\"id\")) ) {\n nid = old.replace( rescape, \"\\\\$&\" );\n } else {\n context.setAttribute( \"id\", nid );\n }\n nid = \"[id='\" + nid + \"'] \";\n\n i = groups.length;\n while ( i-- ) {\n groups[i] = nid + groups[i].join(\"\");\n }\n newContext = rsibling.test( selector ) && context.parentNode || context;\n newSelector = groups.join(\",\");\n }\n\n if ( newSelector ) {\n try {\n push.apply( results, slice.call( newContext.querySelectorAll(\n newSelector\n ), 0 ) );\n return results;\n } catch(qsaError) {\n } finally {\n if ( !old ) {\n context.removeAttribute(\"id\");\n }\n }\n }\n }\n\n return oldSelect( selector, context, results, seed, xml );\n };\n\n if ( matches ) {\n assert(function( div ) {\n // Check to see if it's possible to do matchesSelector\n // on a disconnected node (IE 9)\n disconnectedMatch = matches.call( div, \"div\" );\n\n // This should fail with an exception\n // Gecko does not error, returns false instead\n try {\n matches.call( div, \"[test!='']:sizzle\" );\n rbuggyMatches.push( \"!=\", pseudos );\n } catch ( e ) {}\n });\n\n // rbuggyMatches always contains :active and :focus, so no need for a length check\n rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join(\"|\") );\n\n Sizzle.matchesSelector = function( elem, expr ) {\n // Make sure that attribute selectors are quoted\n expr = expr.replace( rattributeQuotes, \"='$1']\" );\n\n // rbuggyMatches always contains :active, so no need for an existence check\n if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {\n try {\n var ret = matches.call( elem, expr );\n\n // IE 9's matchesSelector returns false on disconnected nodes\n if ( ret || disconnectedMatch ||\n // As well, disconnected nodes are said to be in a document\n // fragment in IE 9\n elem.document && elem.document.nodeType !== 11 ) {\n return ret;\n }\n } catch(e) {}\n }\n\n return Sizzle( expr, null, null, [ elem ] ).length > 0;\n };\n }\n })();\n}\n\n// Deprecated\nExpr.pseudos[\"nth\"] = Expr.pseudos[\"eq\"];\n\n// Back-compat\nfunction setFilters() {}\nExpr.filters = setFilters.prototype = Expr.pseudos;\nExpr.setFilters = new setFilters();\n\n// Override sizzle attribute retrieval\nSizzle.attr = jQuery.attr;\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\njQuery.expr[\":\"] = jQuery.expr.pseudos;\njQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\n\n\n})( window );\nvar runtil = /Until$/,\n rparentsprev = /^(?:parents|prev(?:Until|All))/,\n isSimple = /^.[^:#\\[\\.,]*$/,\n rneedsContext = jQuery.expr.match.needsContext,\n // methods guaranteed to produce a unique set when starting from a unique set\n guaranteedUnique = {\n children: true,\n contents: true,\n next: true,\n prev: true\n };\n\njQuery.fn.extend({\n find: function( selector ) {\n var i, l, length, n, r, ret,\n self = this;\n\n if ( typeof selector !== \"string\" ) {\n return jQuery( selector ).filter(function() {\n for ( i = 0, l = self.length; i < l; i++ ) {\n if ( jQuery.contains( self[ i ], this ) ) {\n return true;\n }\n }\n });\n }\n\n ret = this.pushStack( \"\", \"find\", selector );\n\n for ( i = 0, l = this.length; i < l; i++ ) {\n length = ret.length;\n jQuery.find( selector, this[i], ret );\n\n if ( i > 0 ) {\n // Make sure that the results are unique\n for ( n = length; n < ret.length; n++ ) {\n for ( r = 0; r < length; r++ ) {\n if ( ret[r] === ret[n] ) {\n ret.splice(n--, 1);\n break;\n }\n }\n }\n }\n }\n\n return ret;\n },\n\n has: function( target ) {\n var i,\n targets = jQuery( target, this ),\n len = targets.length;\n\n return this.filter(function() {\n for ( i = 0; i < len; i++ ) {\n if ( jQuery.contains( this, targets[i] ) ) {\n return true;\n }\n }\n });\n },\n\n not: function( selector ) {\n return this.pushStack( winnow(this, selector, false), \"not\", selector);\n },\n\n filter: function( selector ) {\n return this.pushStack( winnow(this, selector, true), \"filter\", selector );\n },\n\n is: function( selector ) {\n return !!selector && (\n typeof selector === \"string\" ?\n // If this is a positional/relative selector, check membership in the returned set\n // so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n rneedsContext.test( selector ) ?\n jQuery( selector, this.context ).index( this[0] ) >= 0 :\n jQuery.filter( selector, this ).length > 0 :\n this.filter( selector ).length > 0 );\n },\n\n closest: function( selectors, context ) {\n var cur,\n i = 0,\n l = this.length,\n ret = [],\n pos = rneedsContext.test( selectors ) || typeof selectors !== \"string\" ?\n jQuery( selectors, context || this.context ) :\n 0;\n\n for ( ; i < l; i++ ) {\n cur = this[i];\n\n while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {\n if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {\n ret.push( cur );\n break;\n }\n cur = cur.parentNode;\n }\n }\n\n ret = ret.length > 1 ? jQuery.unique( ret ) : ret;\n\n return this.pushStack( ret, \"closest\", selectors );\n },\n\n // Determine the position of an element within\n // the matched set of elements\n index: function( elem ) {\n\n // No argument, return index in parent\n if ( !elem ) {\n return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;\n }\n\n // index in selector\n if ( typeof elem === \"string\" ) {\n return jQuery.inArray( this[0], jQuery( elem ) );\n }\n\n // Locate the position of the desired element\n return jQuery.inArray(\n // If it receives a jQuery object, the first element is used\n elem.jquery ? elem[0] : elem, this );\n },\n\n add: function( selector, context ) {\n var set = typeof selector === \"string\" ?\n jQuery( selector, context ) :\n jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),\n all = jQuery.merge( this.get(), set );\n\n return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?\n all :\n jQuery.unique( all ) );\n },\n\n addBack: function( selector ) {\n return this.add( selector == null ?\n this.prevObject : this.prevObject.filter(selector)\n );\n }\n});\n\njQuery.fn.andSelf = jQuery.fn.addBack;\n\n// A painfully simple check to see if an element is disconnected\n// from a document (should be improved, where feasible).\nfunction isDisconnected( node ) {\n return !node || !node.parentNode || node.parentNode.nodeType === 11;\n}\n\nfunction sibling( cur, dir ) {\n do {\n cur = cur[ dir ];\n } while ( cur && cur.nodeType !== 1 );\n\n return cur;\n}\n\njQuery.each({\n parent: function( elem ) {\n var parent = elem.parentNode;\n return parent && parent.nodeType !== 11 ? parent : null;\n },\n parents: function( elem ) {\n return jQuery.dir( elem, \"parentNode\" );\n },\n parentsUntil: function( elem, i, until ) {\n return jQuery.dir( elem, \"parentNode\", until );\n },\n next: function( elem ) {\n return sibling( elem, \"nextSibling\" );\n },\n prev: function( elem ) {\n return sibling( elem, \"previousSibling\" );\n },\n nextAll: function( elem ) {\n return jQuery.dir( elem, \"nextSibling\" );\n },\n prevAll: function( elem ) {\n return jQuery.dir( elem, \"previousSibling\" );\n },\n nextUntil: function( elem, i, until ) {\n return jQuery.dir( elem, \"nextSibling\", until );\n },\n prevUntil: function( elem, i, until ) {\n return jQuery.dir( elem, \"previousSibling\", until );\n },\n siblings: function( elem ) {\n return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );\n },\n children: function( elem ) {\n return jQuery.sibling( elem.firstChild );\n },\n contents: function( elem ) {\n return jQuery.nodeName( elem, \"iframe\" ) ?\n elem.contentDocument || elem.contentWindow.document :\n jQuery.merge( [], elem.childNodes );\n }\n}, function( name, fn ) {\n jQuery.fn[ name ] = function( until, selector ) {\n var ret = jQuery.map( this, fn, until );\n\n if ( !runtil.test( name ) ) {\n selector = until;\n }\n\n if ( selector && typeof selector === \"string\" ) {\n ret = jQuery.filter( selector, ret );\n }\n\n ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;\n\n if ( this.length > 1 && rparentsprev.test( name ) ) {\n ret = ret.reverse();\n }\n\n return this.pushStack( ret, name, core_slice.call( arguments ).join(\",\") );\n };\n});\n\njQuery.extend({\n filter: function( expr, elems, not ) {\n if ( not ) {\n expr = \":not(\" + expr + \")\";\n }\n\n return elems.length === 1 ?\n jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :\n jQuery.find.matches(expr, elems);\n },\n\n dir: function( elem, dir, until ) {\n var matched = [],\n cur = elem[ dir ];\n\n while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {\n if ( cur.nodeType === 1 ) {\n matched.push( cur );\n }\n cur = cur[dir];\n }\n return matched;\n },\n\n sibling: function( n, elem ) {\n var r = [];\n\n for ( ; n; n = n.nextSibling ) {\n if ( n.nodeType === 1 && n !== elem ) {\n r.push( n );\n }\n }\n\n return r;\n }\n});\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, keep ) {\n\n // Can't pass null or undefined to indexOf in Firefox 4\n // Set to 0 to skip string check\n qualifier = qualifier || 0;\n\n if ( jQuery.isFunction( qualifier ) ) {\n return jQuery.grep(elements, function( elem, i ) {\n var retVal = !!qualifier.call( elem, i, elem );\n return retVal === keep;\n });\n\n } else if ( qualifier.nodeType ) {\n return jQuery.grep(elements, function( elem, i ) {\n return ( elem === qualifier ) === keep;\n });\n\n } else if ( typeof qualifier === \"string\" ) {\n var filtered = jQuery.grep(elements, function( elem ) {\n return elem.nodeType === 1;\n });\n\n if ( isSimple.test( qualifier ) ) {\n return jQuery.filter(qualifier, filtered, !keep);\n } else {\n qualifier = jQuery.filter( qualifier, filtered );\n }\n }\n\n return jQuery.grep(elements, function( elem, i ) {\n return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;\n });\n}\nfunction createSafeFragment( document ) {\n var list = nodeNames.split( \"|\" ),\n safeFrag = document.createDocumentFragment();\n\n if ( safeFrag.createElement ) {\n while ( list.length ) {\n safeFrag.createElement(\n list.pop()\n );\n }\n }\n return safeFrag;\n}\n\nvar nodeNames = \"abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|\" +\n \"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video\",\n rinlinejQuery = / jQuery\\d+=\"(?:null|\\d+)\"/g,\n rleadingWhitespace = /^\\s+/,\n rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/gi,\n rtagName = /<([\\w:]+)/,\n rtbody = /<tbody/i,\n rhtml = /<|&#?\\w+;/,\n rnoInnerhtml = /<(?:script|style|link)/i,\n rnocache = /<(?:script|object|embed|option|style)/i,\n rnoshimcache = new RegExp(\"<(?:\" + nodeNames + \")[\\\\s/>]\", \"i\"),\n rcheckableType = /^(?:checkbox|radio)$/,\n // checked=\"checked\" or checked\n rchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n rscriptType = /\\/(java|ecma)script/i,\n rcleanScript = /^\\s*<!(?:\\[CDATA\\[|\\-\\-)|[\\]\\-]{2}>\\s*$/g,\n wrapMap = {\n option: [ 1, \"<select multiple='multiple'>\", \"</select>\" ],\n legend: [ 1, \"<fieldset>\", \"</fieldset>\" ],\n thead: [ 1, \"<table>\", \"</table>\" ],\n tr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n td: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n col: [ 2, \"<table><tbody></tbody><colgroup>\", \"</colgroup></table>\" ],\n area: [ 1, \"<map>\", \"</map>\" ],\n _default: [ 0, \"\", \"\" ]\n },\n safeFragment = createSafeFragment( document ),\n fragmentDiv = safeFragment.appendChild( document.createElement(\"div\") );\n\nwrapMap.optgroup = wrapMap.option;\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,\n// unless wrapped in a div with non-breaking characters in front of it.\nif ( !jQuery.support.htmlSerialize ) {\n wrapMap._default = [ 1, \"X<div>\", \"</div>\" ];\n}\n\njQuery.fn.extend({\n text: function( value ) {\n return jQuery.access( this, function( value ) {\n return value === undefined ?\n jQuery.text( this ) :\n this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );\n }, null, value, arguments.length );\n },\n\n wrapAll: function( html ) {\n if ( jQuery.isFunction( html ) ) {\n return this.each(function(i) {\n jQuery(this).wrapAll( html.call(this, i) );\n });\n }\n\n if ( this[0] ) {\n // The elements to wrap the target around\n var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);\n\n if ( this[0].parentNode ) {\n wrap.insertBefore( this[0] );\n }\n\n wrap.map(function() {\n var elem = this;\n\n while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {\n elem = elem.firstChild;\n }\n\n return elem;\n }).append( this );\n }\n\n return this;\n },\n\n wrapInner: function( html ) {\n if ( jQuery.isFunction( html ) ) {\n return this.each(function(i) {\n jQuery(this).wrapInner( html.call(this, i) );\n });\n }\n\n return this.each(function() {\n var self = jQuery( this ),\n contents = self.contents();\n\n if ( contents.length ) {\n contents.wrapAll( html );\n\n } else {\n self.append( html );\n }\n });\n },\n\n wrap: function( html ) {\n var isFunction = jQuery.isFunction( html );\n\n return this.each(function(i) {\n jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );\n });\n },\n\n unwrap: function() {\n return this.parent().each(function() {\n if ( !jQuery.nodeName( this, \"body\" ) ) {\n jQuery( this ).replaceWith( this.childNodes );\n }\n }).end();\n },\n\n append: function() {\n return this.domManip(arguments, true, function( elem ) {\n if ( this.nodeType === 1 || this.nodeType === 11 ) {\n this.appendChild( elem );\n }\n });\n },\n\n prepend: function() {\n return this.domManip(arguments, true, function( elem ) {\n if ( this.nodeType === 1 || this.nodeType === 11 ) {\n this.insertBefore( elem, this.firstChild );\n }\n });\n },\n\n before: function() {\n if ( !isDisconnected( this[0] ) ) {\n return this.domManip(arguments, false, function( elem ) {\n this.parentNode.insertBefore( elem, this );\n });\n }\n\n if ( arguments.length ) {\n var set = jQuery.clean( arguments );\n return this.pushStack( jQuery.merge( set, this ), \"before\", this.selector );\n }\n },\n\n after: function() {\n if ( !isDisconnected( this[0] ) ) {\n return this.domManip(arguments, false, function( elem ) {\n this.parentNode.insertBefore( elem, this.nextSibling );\n });\n }\n\n if ( arguments.length ) {\n var set = jQuery.clean( arguments );\n return this.pushStack( jQuery.merge( this, set ), \"after\", this.selector );\n }\n },\n\n // keepData is for internal use only--do not document\n remove: function( selector, keepData ) {\n var elem,\n i = 0;\n\n for ( ; (elem = this[i]) != null; i++ ) {\n if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {\n if ( !keepData && elem.nodeType === 1 ) {\n jQuery.cleanData( elem.getElementsByTagName(\"*\") );\n jQuery.cleanData( [ elem ] );\n }\n\n if ( elem.parentNode ) {\n elem.parentNode.removeChild( elem );\n }\n }\n }\n\n return this;\n },\n\n empty: function() {\n var elem,\n i = 0;\n\n for ( ; (elem = this[i]) != null; i++ ) {\n // Remove element nodes and prevent memory leaks\n if ( elem.nodeType === 1 ) {\n jQuery.cleanData( elem.getElementsByTagName(\"*\") );\n }\n\n // Remove any remaining nodes\n while ( elem.firstChild ) {\n elem.removeChild( elem.firstChild );\n }\n }\n\n return this;\n },\n\n clone: function( dataAndEvents, deepDataAndEvents ) {\n dataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n return this.map( function () {\n return jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n });\n },\n\n html: function( value ) {\n return jQuery.access( this, function( value ) {\n var elem = this[0] || {},\n i = 0,\n l = this.length;\n\n if ( value === undefined ) {\n return elem.nodeType === 1 ?\n elem.innerHTML.replace( rinlinejQuery, \"\" ) :\n undefined;\n }\n\n // See if we can take a shortcut and just use innerHTML\n if ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&\n ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&\n !wrapMap[ ( rtagName.exec( value ) || [\"\", \"\"] )[1].toLowerCase() ] ) {\n\n value = value.replace( rxhtmlTag, \"<$1></$2>\" );\n\n try {\n for (; i < l; i++ ) {\n // Remove element nodes and prevent memory leaks\n elem = this[i] || {};\n if ( elem.nodeType === 1 ) {\n jQuery.cleanData( elem.getElementsByTagName( \"*\" ) );\n elem.innerHTML = value;\n }\n }\n\n elem = 0;\n\n // If using innerHTML throws an exception, use the fallback method\n } catch(e) {}\n }\n\n if ( elem ) {\n this.empty().append( value );\n }\n }, null, value, arguments.length );\n },\n\n replaceWith: function( value ) {\n if ( !isDisconnected( this[0] ) ) {\n // Make sure that the elements are removed from the DOM before they are inserted\n // this can help fix replacing a parent with child elements\n if ( jQuery.isFunction( value ) ) {\n return this.each(function(i) {\n var self = jQuery(this), old = self.html();\n self.replaceWith( value.call( this, i, old ) );\n });\n }\n\n if ( typeof value !== \"string\" ) {\n value = jQuery( value ).detach();\n }\n\n return this.each(function() {\n var next = this.nextSibling,\n parent = this.parentNode;\n\n jQuery( this ).remove();\n\n if ( next ) {\n jQuery(next).before( value );\n } else {\n jQuery(parent).append( value );\n }\n });\n }\n\n return this.length ?\n this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), \"replaceWith\", value ) :\n this;\n },\n\n detach: function( selector ) {\n return this.remove( selector, true );\n },\n\n domManip: function( args, table, callback ) {\n\n // Flatten any nested arrays\n args = [].concat.apply( [], args );\n\n var results, first, fragment, iNoClone,\n i = 0,\n value = args[0],\n scripts = [],\n l = this.length;\n\n // We can't cloneNode fragments that contain checked, in WebKit\n if ( !jQuery.support.checkClone && l > 1 && typeof value === \"string\" && rchecked.test( value ) ) {\n return this.each(function() {\n jQuery(this).domManip( args, table, callback );\n });\n }\n\n if ( jQuery.isFunction(value) ) {\n return this.each(function(i) {\n var self = jQuery(this);\n args[0] = value.call( this, i, table ? self.html() : undefined );\n self.domManip( args, table, callback );\n });\n }\n\n if ( this[0] ) {\n results = jQuery.buildFragment( args, this, scripts );\n fragment = results.fragment;\n first = fragment.firstChild;\n\n if ( fragment.childNodes.length === 1 ) {\n fragment = first;\n }\n\n if ( first ) {\n table = table && jQuery.nodeName( first, \"tr\" );\n\n // Use the original fragment for the last item instead of the first because it can end up\n // being emptied incorrectly in certain situations (#8070).\n // Fragments from the fragment cache must always be cloned and never used in place.\n for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {\n callback.call(\n table && jQuery.nodeName( this[i], \"table\" ) ?\n findOrAppend( this[i], \"tbody\" ) :\n this[i],\n i === iNoClone ?\n fragment :\n jQuery.clone( fragment, true, true )\n );\n }\n }\n\n // Fix #11809: Avoid leaking memory\n fragment = first = null;\n\n if ( scripts.length ) {\n jQuery.each( scripts, function( i, elem ) {\n if ( elem.src ) {\n if ( jQuery.ajax ) {\n jQuery.ajax({\n url: elem.src,\n type: \"GET\",\n dataType: \"script\",\n async: false,\n global: false,\n \"throws\": true\n });\n } else {\n jQuery.error(\"no ajax\");\n }\n } else {\n jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || \"\" ).replace( rcleanScript, \"\" ) );\n }\n\n if ( elem.parentNode ) {\n elem.parentNode.removeChild( elem );\n }\n });\n }\n }\n\n return this;\n }\n});\n\nfunction findOrAppend( elem, tag ) {\n return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\n if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {\n return;\n }\n\n var type, i, l,\n oldData = jQuery._data( src ),\n curData = jQuery._data( dest, oldData ),\n events = oldData.events;\n\n if ( events ) {\n delete curData.handle;\n curData.events = {};\n\n for ( type in events ) {\n for ( i = 0, l = events[ type ].length; i < l; i++ ) {\n jQuery.event.add( dest, type, events[ type ][ i ] );\n }\n }\n }\n\n // make the cloned public data object a copy from the original\n if ( curData.data ) {\n curData.data = jQuery.extend( {}, curData.data );\n }\n}\n\nfunction cloneFixAttributes( src, dest ) {\n var nodeName;\n\n // We do not need to do anything for non-Elements\n if ( dest.nodeType !== 1 ) {\n return;\n }\n\n // clearAttributes removes the attributes, which we don't want,\n // but also removes the attachEvent events, which we *do* want\n if ( dest.clearAttributes ) {\n dest.clearAttributes();\n }\n\n // mergeAttributes, in contrast, only merges back on the\n // original attributes, not the events\n if ( dest.mergeAttributes ) {\n dest.mergeAttributes( src );\n }\n\n nodeName = dest.nodeName.toLowerCase();\n\n if ( nodeName === \"object\" ) {\n // IE6-10 improperly clones children of object elements using classid.\n // IE10 throws NoModificationAllowedError if parent is null, #12132.\n if ( dest.parentNode ) {\n dest.outerHTML = src.outerHTML;\n }\n\n // This path appears unavoidable for IE9. When cloning an object\n // element in IE9, the outerHTML strategy above is not sufficient.\n // If the src has innerHTML and the destination does not,\n // copy the src.innerHTML into the dest.innerHTML. #10324\n if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {\n dest.innerHTML = src.innerHTML;\n }\n\n } else if ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n // IE6-8 fails to persist the checked state of a cloned checkbox\n // or radio button. Worse, IE6-7 fail to give the cloned element\n // a checked appearance if the defaultChecked value isn't also set\n\n dest.defaultChecked = dest.checked = src.checked;\n\n // IE6-7 get confused and end up setting the value of a cloned\n // checkbox/radio button to an empty string instead of \"on\"\n if ( dest.value !== src.value ) {\n dest.value = src.value;\n }\n\n // IE6-8 fails to return the selected option to the default selected\n // state when cloning options\n } else if ( nodeName === \"option\" ) {\n dest.selected = src.defaultSelected;\n\n // IE6-8 fails to set the defaultValue to the correct value when\n // cloning other types of input fields\n } else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n dest.defaultValue = src.defaultValue;\n\n // IE blanks contents when cloning scripts\n } else if ( nodeName === \"script\" && dest.text !== src.text ) {\n dest.text = src.text;\n }\n\n // Event data gets referenced instead of copied if the expando\n // gets copied too\n dest.removeAttribute( jQuery.expando );\n}\n\njQuery.buildFragment = function( args, context, scripts ) {\n var fragment, cacheable, cachehit,\n first = args[ 0 ];\n\n // Set context from what may come in as undefined or a jQuery collection or a node\n // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 &\n // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception\n context = context || document;\n context = !context.nodeType && context[0] || context;\n context = context.ownerDocument || context;\n\n // Only cache \"small\" (1/2 KB) HTML strings that are associated with the main document\n // Cloning options loses the selected state, so don't cache them\n // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment\n // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache\n // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501\n if ( args.length === 1 && typeof first === \"string\" && first.length < 512 && context === document &&\n first.charAt(0) === \"<\" && !rnocache.test( first ) &&\n (jQuery.support.checkClone || !rchecked.test( first )) &&\n (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {\n\n // Mark cacheable and look for a hit\n cacheable = true;\n fragment = jQuery.fragments[ first ];\n cachehit = fragment !== undefined;\n }\n\n if ( !fragment ) {\n fragment = context.createDocumentFragment();\n jQuery.clean( args, context, fragment, scripts );\n\n // Update the cache, but only store false\n // unless this is a second parsing of the same content\n if ( cacheable ) {\n jQuery.fragments[ first ] = cachehit && fragment;\n }\n }\n\n return { fragment: fragment, cacheable: cacheable };\n};\n\njQuery.fragments = {};\n\njQuery.each({\n appendTo: \"append\",\n prependTo: \"prepend\",\n insertBefore: \"before\",\n insertAfter: \"after\",\n replaceAll: \"replaceWith\"\n}, function( name, original ) {\n jQuery.fn[ name ] = function( selector ) {\n var elems,\n i = 0,\n ret = [],\n insert = jQuery( selector ),\n l = insert.length,\n parent = this.length === 1 && this[0].parentNode;\n\n if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) {\n insert[ original ]( this[0] );\n return this;\n } else {\n for ( ; i < l; i++ ) {\n elems = ( i > 0 ? this.clone(true) : this ).get();\n jQuery( insert[i] )[ original ]( elems );\n ret = ret.concat( elems );\n }\n\n return this.pushStack( ret, name, insert.selector );\n }\n };\n});\n\nfunction getAll( elem ) {\n if ( typeof elem.getElementsByTagName !== \"undefined\" ) {\n return elem.getElementsByTagName( \"*\" );\n\n } else if ( typeof elem.querySelectorAll !== \"undefined\" ) {\n return elem.querySelectorAll( \"*\" );\n\n } else {\n return [];\n }\n}\n\n// Used in clean, fixes the defaultChecked property\nfunction fixDefaultChecked( elem ) {\n if ( rcheckableType.test( elem.type ) ) {\n elem.defaultChecked = elem.checked;\n }\n}\n\njQuery.extend({\n clone: function( elem, dataAndEvents, deepDataAndEvents ) {\n var srcElements,\n destElements,\n i,\n clone;\n\n if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( \"<\" + elem.nodeName + \">\" ) ) {\n clone = elem.cloneNode( true );\n\n // IE<=8 does not properly clone detached, unknown element nodes\n } else {\n fragmentDiv.innerHTML = elem.outerHTML;\n fragmentDiv.removeChild( clone = fragmentDiv.firstChild );\n }\n\n if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&\n (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {\n // IE copies events bound via attachEvent when using cloneNode.\n // Calling detachEvent on the clone will also remove the events\n // from the original. In order to get around this, we use some\n // proprietary methods to clear the events. Thanks to MooTools\n // guys for this hotness.\n\n cloneFixAttributes( elem, clone );\n\n // Using Sizzle here is crazy slow, so we use getElementsByTagName instead\n srcElements = getAll( elem );\n destElements = getAll( clone );\n\n // Weird iteration because IE will replace the length property\n // with an element if you are cloning the body and one of the\n // elements on the page has a name or id of \"length\"\n for ( i = 0; srcElements[i]; ++i ) {\n // Ensure that the destination node is not null; Fixes #9587\n if ( destElements[i] ) {\n cloneFixAttributes( srcElements[i], destElements[i] );\n }\n }\n }\n\n // Copy the events from the original to the clone\n if ( dataAndEvents ) {\n cloneCopyEvent( elem, clone );\n\n if ( deepDataAndEvents ) {\n srcElements = getAll( elem );\n destElements = getAll( clone );\n\n for ( i = 0; srcElements[i]; ++i ) {\n cloneCopyEvent( srcElements[i], destElements[i] );\n }\n }\n }\n\n srcElements = destElements = null;\n\n // Return the cloned set\n return clone;\n },\n\n clean: function( elems, context, fragment, scripts ) {\n var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,\n safe = context === document && safeFragment,\n ret = [];\n\n // Ensure that context is a document\n if ( !context || typeof context.createDocumentFragment === \"undefined\" ) {\n context = document;\n }\n\n // Use the already-created safe fragment if context permits\n for ( i = 0; (elem = elems[i]) != null; i++ ) {\n if ( typeof elem === \"number\" ) {\n elem += \"\";\n }\n\n if ( !elem ) {\n continue;\n }\n\n // Convert html string into DOM nodes\n if ( typeof elem === \"string\" ) {\n if ( !rhtml.test( elem ) ) {\n elem = context.createTextNode( elem );\n } else {\n // Ensure a safe container in which to render the html\n safe = safe || createSafeFragment( context );\n div = context.createElement(\"div\");\n safe.appendChild( div );\n\n // Fix \"XHTML\"-style tags in all browsers\n elem = elem.replace(rxhtmlTag, \"<$1></$2>\");\n\n // Go to html and back, then peel off extra wrappers\n tag = ( rtagName.exec( elem ) || [\"\", \"\"] )[1].toLowerCase();\n wrap = wrapMap[ tag ] || wrapMap._default;\n depth = wrap[0];\n div.innerHTML = wrap[1] + elem + wrap[2];\n\n // Move to the right depth\n while ( depth-- ) {\n div = div.lastChild;\n }\n\n // Remove IE's autoinserted <tbody> from table fragments\n if ( !jQuery.support.tbody ) {\n\n // String was a <table>, *may* have spurious <tbody>\n hasBody = rtbody.test(elem);\n tbody = tag === \"table\" && !hasBody ?\n div.firstChild && div.firstChild.childNodes :\n\n // String was a bare <thead> or <tfoot>\n wrap[1] === \"<table>\" && !hasBody ?\n div.childNodes :\n [];\n\n for ( j = tbody.length - 1; j >= 0 ; --j ) {\n if ( jQuery.nodeName( tbody[ j ], \"tbody\" ) && !tbody[ j ].childNodes.length ) {\n tbody[ j ].parentNode.removeChild( tbody[ j ] );\n }\n }\n }\n\n // IE completely kills leading whitespace when innerHTML is used\n if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {\n div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );\n }\n\n elem = div.childNodes;\n\n // Take out of fragment container (we need a fresh div each time)\n div.parentNode.removeChild( div );\n }\n }\n\n if ( elem.nodeType ) {\n ret.push( elem );\n } else {\n jQuery.merge( ret, elem );\n }\n }\n\n // Fix #11356: Clear elements from safeFragment\n if ( div ) {\n elem = div = safe = null;\n }\n\n // Reset defaultChecked for any radios and checkboxes\n // about to be appended to the DOM in IE 6/7 (#8060)\n if ( !jQuery.support.appendChecked ) {\n for ( i = 0; (elem = ret[i]) != null; i++ ) {\n if ( jQuery.nodeName( elem, \"input\" ) ) {\n fixDefaultChecked( elem );\n } else if ( typeof elem.getElementsByTagName !== \"undefined\" ) {\n jQuery.grep( elem.getElementsByTagName(\"input\"), fixDefaultChecked );\n }\n }\n }\n\n // Append elements to a provided document fragment\n if ( fragment ) {\n // Special handling of each script element\n handleScript = function( elem ) {\n // Check if we consider it executable\n if ( !elem.type || rscriptType.test( elem.type ) ) {\n // Detach the script and store it in the scripts array (if provided) or the fragment\n // Return truthy to indicate that it has been handled\n return scripts ?\n scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :\n fragment.appendChild( elem );\n }\n };\n\n for ( i = 0; (elem = ret[i]) != null; i++ ) {\n // Check if we're done after handling an executable script\n if ( !( jQuery.nodeName( elem, \"script\" ) && handleScript( elem ) ) ) {\n // Append to fragment and handle embedded scripts\n fragment.appendChild( elem );\n if ( typeof elem.getElementsByTagName !== \"undefined\" ) {\n // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration\n jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName(\"script\") ), handleScript );\n\n // Splice the scripts into ret after their former ancestor and advance our index beyond them\n ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );\n i += jsTags.length;\n }\n }\n }\n }\n\n return ret;\n },\n\n cleanData: function( elems, /* internal */ acceptData ) {\n var data, id, elem, type,\n i = 0,\n internalKey = jQuery.expando,\n cache = jQuery.cache,\n deleteExpando = jQuery.support.deleteExpando,\n special = jQuery.event.special;\n\n for ( ; (elem = elems[i]) != null; i++ ) {\n\n if ( acceptData || jQuery.acceptData( elem ) ) {\n\n id = elem[ internalKey ];\n data = id && cache[ id ];\n\n if ( data ) {\n if ( data.events ) {\n for ( type in data.events ) {\n if ( special[ type ] ) {\n jQuery.event.remove( elem, type );\n\n // This is a shortcut to avoid jQuery.event.remove's overhead\n } else {\n jQuery.removeEvent( elem, type, data.handle );\n }\n }\n }\n\n // Remove cache only if it was not already removed by jQuery.event.remove\n if ( cache[ id ] ) {\n\n delete cache[ id ];\n\n // IE does not allow us to delete expando properties from nodes,\n // nor does it have a removeAttribute function on Document nodes;\n // we must handle all of these cases\n if ( deleteExpando ) {\n delete elem[ internalKey ];\n\n } else if ( elem.removeAttribute ) {\n elem.removeAttribute( internalKey );\n\n } else {\n elem[ internalKey ] = null;\n }\n\n jQuery.deletedIds.push( id );\n }\n }\n }\n }\n }\n});\n// Limit scope pollution from any deprecated API\n(function() {\n\nvar matched, browser;\n\n// Use of jQuery.browser is frowned upon.\n// More details: http://api.jquery.com/jQuery.browser\n// jQuery.uaMatch maintained for back-compat\njQuery.uaMatch = function( ua ) {\n ua = ua.toLowerCase();\n\n var match = /(chrome)[ \\/]([\\w.]+)/.exec( ua ) ||\n /(webkit)[ \\/]([\\w.]+)/.exec( ua ) ||\n /(opera)(?:.*version|)[ \\/]([\\w.]+)/.exec( ua ) ||\n /(msie) ([\\w.]+)/.exec( ua ) ||\n ua.indexOf(\"compatible\") < 0 && /(mozilla)(?:.*? rv:([\\w.]+)|)/.exec( ua ) ||\n [];\n\n return {\n browser: match[ 1 ] || \"\",\n version: match[ 2 ] || \"0\"\n };\n};\n\nmatched = jQuery.uaMatch( navigator.userAgent );\nbrowser = {};\n\nif ( matched.browser ) {\n browser[ matched.browser ] = true;\n browser.version = matched.version;\n}\n\n// Chrome is Webkit, but Webkit is also Safari.\nif ( browser.chrome ) {\n browser.webkit = true;\n} else if ( browser.webkit ) {\n browser.safari = true;\n}\n\njQuery.browser = browser;\n\njQuery.sub = function() {\n function jQuerySub( selector, context ) {\n return new jQuerySub.fn.init( selector, context );\n }\n jQuery.extend( true, jQuerySub, this );\n jQuerySub.superclass = this;\n jQuerySub.fn = jQuerySub.prototype = this();\n jQuerySub.fn.constructor = jQuerySub;\n jQuerySub.sub = this.sub;\n jQuerySub.fn.init = function init( selector, context ) {\n if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {\n context = jQuerySub( context );\n }\n\n return jQuery.fn.init.call( this, selector, context, rootjQuerySub );\n };\n jQuerySub.fn.init.prototype = jQuerySub.fn;\n var rootjQuerySub = jQuerySub(document);\n return jQuerySub;\n};\n\n})();\nvar curCSS, iframe, iframeDoc,\n ralpha = /alpha\\([^)]*\\)/i,\n ropacity = /opacity=([^)]*)/,\n rposition = /^(top|right|bottom|left)$/,\n // swappable if display is none or starts with table except \"table\", \"table-cell\", or \"table-caption\"\n // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display\n rdisplayswap = /^(none|table(?!-c[ea]).+)/,\n rmargin = /^margin/,\n rnumsplit = new RegExp( \"^(\" + core_pnum + \")(.*)$\", \"i\" ),\n rnumnonpx = new RegExp( \"^(\" + core_pnum + \")(?!px)[a-z%]+$\", \"i\" ),\n rrelNum = new RegExp( \"^([-+])=(\" + core_pnum + \")\", \"i\" ),\n elemdisplay = { BODY: \"block\" },\n\n cssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n cssNormalTransform = {\n letterSpacing: 0,\n fontWeight: 400\n },\n\n cssExpand = [ \"Top\", \"Right\", \"Bottom\", \"Left\" ],\n cssPrefixes = [ \"Webkit\", \"O\", \"Moz\", \"ms\" ],\n\n eventsToggle = jQuery.fn.toggle;\n\n// return a css property mapped to a potentially vendor prefixed property\nfunction vendorPropName( style, name ) {\n\n // shortcut for names that are not vendor prefixed\n if ( name in style ) {\n return name;\n }\n\n // check for vendor prefixed names\n var capName = name.charAt(0).toUpperCase() + name.slice(1),\n origName = name,\n i = cssPrefixes.length;\n\n while ( i-- ) {\n name = cssPrefixes[ i ] + capName;\n if ( name in style ) {\n return name;\n }\n }\n\n return origName;\n}\n\nfunction isHidden( elem, el ) {\n elem = el || elem;\n return jQuery.css( elem, \"display\" ) === \"none\" || !jQuery.contains( elem.ownerDocument, elem );\n}\n\nfunction showHide( elements, show ) {\n var elem, display,\n values = [],\n index = 0,\n length = elements.length;\n\n for ( ; index < length; index++ ) {\n elem = elements[ index ];\n if ( !elem.style ) {\n continue;\n }\n values[ index ] = jQuery._data( elem, \"olddisplay\" );\n if ( show ) {\n // Reset the inline display of this element to learn if it is\n // being hidden by cascaded rules or not\n if ( !values[ index ] && elem.style.display === \"none\" ) {\n elem.style.display = \"\";\n }\n\n // Set elements which have been overridden with display: none\n // in a stylesheet to whatever the default browser style is\n // for such an element\n if ( elem.style.display === \"\" && isHidden( elem ) ) {\n values[ index ] = jQuery._data( elem, \"olddisplay\", css_defaultDisplay(elem.nodeName) );\n }\n } else {\n display = curCSS( elem, \"display\" );\n\n if ( !values[ index ] && display !== \"none\" ) {\n jQuery._data( elem, \"olddisplay\", display );\n }\n }\n }\n\n // Set the display of most of the elements in a second loop\n // to avoid the constant reflow\n for ( index = 0; index < length; index++ ) {\n elem = elements[ index ];\n if ( !elem.style ) {\n continue;\n }\n if ( !show || elem.style.display === \"none\" || elem.style.display === \"\" ) {\n elem.style.display = show ? values[ index ] || \"\" : \"none\";\n }\n }\n\n return elements;\n}\n\njQuery.fn.extend({\n css: function( name, value ) {\n return jQuery.access( this, function( elem, name, value ) {\n return value !== undefined ?\n jQuery.style( elem, name, value ) :\n jQuery.css( elem, name );\n }, name, value, arguments.length > 1 );\n },\n show: function() {\n return showHide( this, true );\n },\n hide: function() {\n return showHide( this );\n },\n toggle: function( state, fn2 ) {\n var bool = typeof state === \"boolean\";\n\n if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {\n return eventsToggle.apply( this, arguments );\n }\n\n return this.each(function() {\n if ( bool ? state : isHidden( this ) ) {\n jQuery( this ).show();\n } else {\n jQuery( this ).hide();\n }\n });\n }\n});\n\njQuery.extend({\n // Add in style property hooks for overriding the default\n // behavior of getting and setting a style property\n cssHooks: {\n opacity: {\n get: function( elem, computed ) {\n if ( computed ) {\n // We should always get a number back from opacity\n var ret = curCSS( elem, \"opacity\" );\n return ret === \"\" ? \"1\" : ret;\n\n }\n }\n }\n },\n\n // Exclude the following css properties to add px\n cssNumber: {\n \"fillOpacity\": true,\n \"fontWeight\": true,\n \"lineHeight\": true,\n \"opacity\": true,\n \"orphans\": true,\n \"widows\": true,\n \"zIndex\": true,\n \"zoom\": true\n },\n\n // Add in properties whose names you wish to fix before\n // setting or getting the value\n cssProps: {\n // normalize float css property\n \"float\": jQuery.support.cssFloat ? \"cssFloat\" : \"styleFloat\"\n },\n\n // Get and set the style property on a DOM Node\n style: function( elem, name, value, extra ) {\n // Don't set styles on text and comment nodes\n if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n return;\n }\n\n // Make sure that we're working with the right name\n var ret, type, hooks,\n origName = jQuery.camelCase( name ),\n style = elem.style;\n\n name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );\n\n // gets hook for the prefixed version\n // followed by the unprefixed version\n hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n // Check if we're setting a value\n if ( value !== undefined ) {\n type = typeof value;\n\n // convert relative number strings (+= or -=) to relative numbers. #7345\n if ( type === \"string\" && (ret = rrelNum.exec( value )) ) {\n value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );\n // Fixes bug #9237\n type = \"number\";\n }\n\n // Make sure that NaN and null values aren't set. See: #7116\n if ( value == null || type === \"number\" && isNaN( value ) ) {\n return;\n }\n\n // If a number was passed in, add 'px' to the (except for certain CSS properties)\n if ( type === \"number\" && !jQuery.cssNumber[ origName ] ) {\n value += \"px\";\n }\n\n // If a hook was provided, use that value, otherwise just set the specified value\n if ( !hooks || !(\"set\" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {\n // Wrapped to prevent IE from throwing errors when 'invalid' values are provided\n // Fixes bug #5509\n try {\n style[ name ] = value;\n } catch(e) {}\n }\n\n } else {\n // If a hook was provided get the non-computed value from there\n if ( hooks && \"get\" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {\n return ret;\n }\n\n // Otherwise just get the value from the style object\n return style[ name ];\n }\n },\n\n css: function( elem, name, numeric, extra ) {\n var val, num, hooks,\n origName = jQuery.camelCase( name );\n\n // Make sure that we're working with the right name\n name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );\n\n // gets hook for the prefixed version\n // followed by the unprefixed version\n hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n // If a hook was provided get the computed value from there\n if ( hooks && \"get\" in hooks ) {\n val = hooks.get( elem, true, extra );\n }\n\n // Otherwise, if a way to get the computed value exists, use that\n if ( val === undefined ) {\n val = curCSS( elem, name );\n }\n\n //convert \"normal\" to computed value\n if ( val === \"normal\" && name in cssNormalTransform ) {\n val = cssNormalTransform[ name ];\n }\n\n // Return, converting to number if forced or a qualifier was provided and val looks numeric\n if ( numeric || extra !== undefined ) {\n num = parseFloat( val );\n return numeric || jQuery.isNumeric( num ) ? num || 0 : val;\n }\n return val;\n },\n\n // A method for quickly swapping in/out CSS properties to get correct calculations\n swap: function( elem, options, callback ) {\n var ret, name,\n old = {};\n\n // Remember the old values, and insert the new ones\n for ( name in options ) {\n old[ name ] = elem.style[ name ];\n elem.style[ name ] = options[ name ];\n }\n\n ret = callback.call( elem );\n\n // Revert the old values\n for ( name in options ) {\n elem.style[ name ] = old[ name ];\n }\n\n return ret;\n }\n});\n\n// NOTE: To any future maintainer, we've window.getComputedStyle\n// because jsdom on node.js will break without it.\nif ( window.getComputedStyle ) {\n curCSS = function( elem, name ) {\n var ret, width, minWidth, maxWidth,\n computed = window.getComputedStyle( elem, null ),\n style = elem.style;\n\n if ( computed ) {\n\n // getPropertyValue is only needed for .css('filter') in IE9, see #12537\n ret = computed.getPropertyValue( name ) || computed[ name ];\n\n if ( ret === \"\" && !jQuery.contains( elem.ownerDocument, elem ) ) {\n ret = jQuery.style( elem, name );\n }\n\n // A tribute to the \"awesome hack by Dean Edwards\"\n // Chrome < 17 and Safari 5.0 uses \"computed value\" instead of \"used value\" for margin-right\n // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels\n // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values\n if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {\n width = style.width;\n minWidth = style.minWidth;\n maxWidth = style.maxWidth;\n\n style.minWidth = style.maxWidth = style.width = ret;\n ret = computed.width;\n\n style.width = width;\n style.minWidth = minWidth;\n style.maxWidth = maxWidth;\n }\n }\n\n return ret;\n };\n} else if ( document.documentElement.currentStyle ) {\n curCSS = function( elem, name ) {\n var left, rsLeft,\n ret = elem.currentStyle && elem.currentStyle[ name ],\n style = elem.style;\n\n // Avoid setting ret to empty string here\n // so we don't default to auto\n if ( ret == null && style && style[ name ] ) {\n ret = style[ name ];\n }\n\n // From the awesome hack by Dean Edwards\n // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291\n\n // If we're not dealing with a regular pixel number\n // but a number that has a weird ending, we need to convert it to pixels\n // but not position css attributes, as those are proportional to the parent element instead\n // and we can't measure the parent instead because it might trigger a \"stacking dolls\" problem\n if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {\n\n // Remember the original values\n left = style.left;\n rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;\n\n // Put in the new values to get a computed value out\n if ( rsLeft ) {\n elem.runtimeStyle.left = elem.currentStyle.left;\n }\n style.left = name === \"fontSize\" ? \"1em\" : ret;\n ret = style.pixelLeft + \"px\";\n\n // Revert the changed values\n style.left = left;\n if ( rsLeft ) {\n elem.runtimeStyle.left = rsLeft;\n }\n }\n\n return ret === \"\" ? \"auto\" : ret;\n };\n}\n\nfunction setPositiveNumber( elem, value, subtract ) {\n var matches = rnumsplit.exec( value );\n return matches ?\n Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || \"px\" ) :\n value;\n}\n\nfunction augmentWidthOrHeight( elem, name, extra, isBorderBox ) {\n var i = extra === ( isBorderBox ? \"border\" : \"content\" ) ?\n // If we already have the right measurement, avoid augmentation\n 4 :\n // Otherwise initialize for horizontal or vertical properties\n name === \"width\" ? 1 : 0,\n\n val = 0;\n\n for ( ; i < 4; i += 2 ) {\n // both box models exclude margin, so add it if we want it\n if ( extra === \"margin\" ) {\n // we use jQuery.css instead of curCSS here\n // because of the reliableMarginRight CSS hook!\n val += jQuery.css( elem, extra + cssExpand[ i ], true );\n }\n\n // From this point on we use curCSS for maximum performance (relevant in animations)\n if ( isBorderBox ) {\n // border-box includes padding, so remove it if we want content\n if ( extra === \"content\" ) {\n val -= parseFloat( curCSS( elem, \"padding\" + cssExpand[ i ] ) ) || 0;\n }\n\n // at this point, extra isn't border nor margin, so remove border\n if ( extra !== \"margin\" ) {\n val -= parseFloat( curCSS( elem, \"border\" + cssExpand[ i ] + \"Width\" ) ) || 0;\n }\n } else {\n // at this point, extra isn't content, so add padding\n val += parseFloat( curCSS( elem, \"padding\" + cssExpand[ i ] ) ) || 0;\n\n // at this point, extra isn't content nor padding, so add border\n if ( extra !== \"padding\" ) {\n val += parseFloat( curCSS( elem, \"border\" + cssExpand[ i ] + \"Width\" ) ) || 0;\n }\n }\n }\n\n return val;\n}\n\nfunction getWidthOrHeight( elem, name, extra ) {\n\n // Start with offset property, which is equivalent to the border-box value\n var val = name === \"width\" ? elem.offsetWidth : elem.offsetHeight,\n valueIsBorderBox = true,\n isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, \"boxSizing\" ) === \"border-box\";\n\n // some non-html elements return undefined for offsetWidth, so check for null/undefined\n // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285\n // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668\n if ( val <= 0 || val == null ) {\n // Fall back to computed then uncomputed css if necessary\n val = curCSS( elem, name );\n if ( val < 0 || val == null ) {\n val = elem.style[ name ];\n }\n\n // Computed unit is not pixels. Stop here and return.\n if ( rnumnonpx.test(val) ) {\n return val;\n }\n\n // we need the check for style in case a browser which returns unreliable values\n // for getComputedStyle silently falls back to the reliable elem.style\n valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );\n\n // Normalize \"\", auto, and prepare for extra\n val = parseFloat( val ) || 0;\n }\n\n // use the active box-sizing model to add/subtract irrelevant styles\n return ( val +\n augmentWidthOrHeight(\n elem,\n name,\n extra || ( isBorderBox ? \"border\" : \"content\" ),\n valueIsBorderBox\n )\n ) + \"px\";\n}\n\n\n// Try to determine the default display value of an element\nfunction css_defaultDisplay( nodeName ) {\n if ( elemdisplay[ nodeName ] ) {\n return elemdisplay[ nodeName ];\n }\n\n var elem = jQuery( \"<\" + nodeName + \">\" ).appendTo( document.body ),\n display = elem.css(\"display\");\n elem.remove();\n\n // If the simple way fails,\n // get element's real default display by attaching it to a temp iframe\n if ( display === \"none\" || display === \"\" ) {\n // Use the already-created iframe if possible\n iframe = document.body.appendChild(\n iframe || jQuery.extend( document.createElement(\"iframe\"), {\n frameBorder: 0,\n width: 0,\n height: 0\n })\n );\n\n // Create a cacheable copy of the iframe document on first call.\n // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML\n // document to it; WebKit & Firefox won't allow reusing the iframe document.\n if ( !iframeDoc || !iframe.createElement ) {\n iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;\n iframeDoc.write(\"<!doctype html><html><body>\");\n iframeDoc.close();\n }\n\n elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );\n\n display = curCSS( elem, \"display\" );\n document.body.removeChild( iframe );\n }\n\n // Store the correct default display\n elemdisplay[ nodeName ] = display;\n\n return display;\n}\n\njQuery.each([ \"height\", \"width\" ], function( i, name ) {\n jQuery.cssHooks[ name ] = {\n get: function( elem, computed, extra ) {\n if ( computed ) {\n // certain elements can have dimension info if we invisibly show them\n // however, it must have a current display style that would benefit from this\n if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, \"display\" ) ) ) {\n return jQuery.swap( elem, cssShow, function() {\n return getWidthOrHeight( elem, name, extra );\n });\n } else {\n return getWidthOrHeight( elem, name, extra );\n }\n }\n },\n\n set: function( elem, value, extra ) {\n return setPositiveNumber( elem, value, extra ?\n augmentWidthOrHeight(\n elem,\n name,\n extra,\n jQuery.support.boxSizing && jQuery.css( elem, \"boxSizing\" ) === \"border-box\"\n ) : 0\n );\n }\n };\n});\n\nif ( !jQuery.support.opacity ) {\n jQuery.cssHooks.opacity = {\n get: function( elem, computed ) {\n // IE uses filters for opacity\n return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || \"\" ) ?\n ( 0.01 * parseFloat( RegExp.$1 ) ) + \"\" :\n computed ? \"1\" : \"\";\n },\n\n set: function( elem, value ) {\n var style = elem.style,\n currentStyle = elem.currentStyle,\n opacity = jQuery.isNumeric( value ) ? \"alpha(opacity=\" + value * 100 + \")\" : \"\",\n filter = currentStyle && currentStyle.filter || style.filter || \"\";\n\n // IE has trouble with opacity if it does not have layout\n // Force it by setting the zoom level\n style.zoom = 1;\n\n // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652\n if ( value >= 1 && jQuery.trim( filter.replace( ralpha, \"\" ) ) === \"\" &&\n style.removeAttribute ) {\n\n // Setting style.filter to null, \"\" & \" \" still leave \"filter:\" in the cssText\n // if \"filter:\" is present at all, clearType is disabled, we want to avoid this\n // style.removeAttribute is IE Only, but so apparently is this code path...\n style.removeAttribute( \"filter\" );\n\n // if there there is no filter style applied in a css rule, we are done\n if ( currentStyle && !currentStyle.filter ) {\n return;\n }\n }\n\n // otherwise, set new filter values\n style.filter = ralpha.test( filter ) ?\n filter.replace( ralpha, opacity ) :\n filter + \" \" + opacity;\n }\n };\n}\n\n// These hooks cannot be added until DOM ready because the support test\n// for it is not run until after DOM ready\njQuery(function() {\n if ( !jQuery.support.reliableMarginRight ) {\n jQuery.cssHooks.marginRight = {\n get: function( elem, computed ) {\n // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right\n // Work around by temporarily setting element display to inline-block\n return jQuery.swap( elem, { \"display\": \"inline-block\" }, function() {\n if ( computed ) {\n return curCSS( elem, \"marginRight\" );\n }\n });\n }\n };\n }\n\n // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084\n // getComputedStyle returns percent when specified for top/left/bottom/right\n // rather than make the css module depend on the offset module, we just check for it here\n if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {\n jQuery.each( [ \"top\", \"left\" ], function( i, prop ) {\n jQuery.cssHooks[ prop ] = {\n get: function( elem, computed ) {\n if ( computed ) {\n var ret = curCSS( elem, prop );\n // if curCSS returns percentage, fallback to offset\n return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + \"px\" : ret;\n }\n }\n };\n });\n }\n\n});\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n jQuery.expr.filters.hidden = function( elem ) {\n return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, \"display\" )) === \"none\");\n };\n\n jQuery.expr.filters.visible = function( elem ) {\n return !jQuery.expr.filters.hidden( elem );\n };\n}\n\n// These hooks are used by animate to expand properties\njQuery.each({\n margin: \"\",\n padding: \"\",\n border: \"Width\"\n}, function( prefix, suffix ) {\n jQuery.cssHooks[ prefix + suffix ] = {\n expand: function( value ) {\n var i,\n\n // assumes a single number if not a string\n parts = typeof value === \"string\" ? value.split(\" \") : [ value ],\n expanded = {};\n\n for ( i = 0; i < 4; i++ ) {\n expanded[ prefix + cssExpand[ i ] + suffix ] =\n parts[ i ] || parts[ i - 2 ] || parts[ 0 ];\n }\n\n return expanded;\n }\n };\n\n if ( !rmargin.test( prefix ) ) {\n jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;\n }\n});\nvar r20 = /%20/g,\n rbracket = /\\[\\]$/,\n rCRLF = /\\r?\\n/g,\n rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,\n rselectTextarea = /^(?:select|textarea)/i;\n\njQuery.fn.extend({\n serialize: function() {\n return jQuery.param( this.serializeArray() );\n },\n serializeArray: function() {\n return this.map(function(){\n return this.elements ? jQuery.makeArray( this.elements ) : this;\n })\n .filter(function(){\n return this.name && !this.disabled &&\n ( this.checked || rselectTextarea.test( this.nodeName ) ||\n rinput.test( this.type ) );\n })\n .map(function( i, elem ){\n var val = jQuery( this ).val();\n\n return val == null ?\n null :\n jQuery.isArray( val ) ?\n jQuery.map( val, function( val, i ){\n return { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n }) :\n { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n }).get();\n }\n});\n\n//Serialize an array of form elements or a set of\n//key/values into a query string\njQuery.param = function( a, traditional ) {\n var prefix,\n s = [],\n add = function( key, value ) {\n // If value is a function, invoke it and return its value\n value = jQuery.isFunction( value ) ? value() : ( value == null ? \"\" : value );\n s[ s.length ] = encodeURIComponent( key ) + \"=\" + encodeURIComponent( value );\n };\n\n // Set traditional to true for jQuery <= 1.3.2 behavior.\n if ( traditional === undefined ) {\n traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;\n }\n\n // If an array was passed in, assume that it is an array of form elements.\n if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n // Serialize the form elements\n jQuery.each( a, function() {\n add( this.name, this.value );\n });\n\n } else {\n // If traditional, encode the \"old\" way (the way 1.3.2 or older\n // did it), otherwise encode params recursively.\n for ( prefix in a ) {\n buildParams( prefix, a[ prefix ], traditional, add );\n }\n }\n\n // Return the resulting serialization\n return s.join( \"&\" ).replace( r20, \"+\" );\n};\n\nfunction buildParams( prefix, obj, traditional, add ) {\n var name;\n\n if ( jQuery.isArray( obj ) ) {\n // Serialize array item.\n jQuery.each( obj, function( i, v ) {\n if ( traditional || rbracket.test( prefix ) ) {\n // Treat each array item as a scalar.\n add( prefix, v );\n\n } else {\n // If array item is non-scalar (array or object), encode its\n // numeric index to resolve deserialization ambiguity issues.\n // Note that rack (as of 1.0.0) can't currently deserialize\n // nested arrays properly, and attempting to do so may cause\n // a server error. Possible fixes are to modify rack's\n // deserialization algorithm or to provide an option or flag\n // to force array serialization to be shallow.\n buildParams( prefix + \"[\" + ( typeof v === \"object\" ? i : \"\" ) + \"]\", v, traditional, add );\n }\n });\n\n } else if ( !traditional && jQuery.type( obj ) === \"object\" ) {\n // Serialize object item.\n for ( name in obj ) {\n buildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n }\n\n } else {\n // Serialize scalar item.\n add( prefix, obj );\n }\n}\nvar\n // Document location\n ajaxLocParts,\n ajaxLocation,\n\n rhash = /#.*$/,\n rheaders = /^(.*?):[ \\t]*([^\\r\\n]*)\\r?$/mg, // IE leaves an \\r character at EOL\n // #7653, #8125, #8152: local protocol detection\n rlocalProtocol = /^(?:about|app|app\\-storage|.+\\-extension|file|res|widget):$/,\n rnoContent = /^(?:GET|HEAD)$/,\n rprotocol = /^\\/\\//,\n rquery = /\\?/,\n rscript = /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n rts = /([?&])_=[^&]*/,\n rurl = /^([\\w\\+\\.\\-]+:)(?:\\/\\/([^\\/?#:]*)(?::(\\d+)|)|)/,\n\n // Keep a copy of the old load method\n _load = jQuery.fn.load,\n\n /* Prefilters\n * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n * 2) These are called:\n * - BEFORE asking for a transport\n * - AFTER param serialization (s.data is a string if s.processData is true)\n * 3) key is the dataType\n * 4) the catchall symbol \"*\" can be used\n * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n */\n prefilters = {},\n\n /* Transports bindings\n * 1) key is the dataType\n * 2) the catchall symbol \"*\" can be used\n * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n */\n transports = {},\n\n // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression\n allTypes = [\"*/\"] + [\"*\"];\n\n// #8138, IE may throw an exception when accessing\n// a field from window.location if document.domain has been set\ntry {\n ajaxLocation = location.href;\n} catch( e ) {\n // Use the href attribute of an A element\n // since IE will modify it given document.location\n ajaxLocation = document.createElement( \"a\" );\n ajaxLocation.href = \"\";\n ajaxLocation = ajaxLocation.href;\n}\n\n// Segment location into parts\najaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n // dataTypeExpression is optional and defaults to \"*\"\n return function( dataTypeExpression, func ) {\n\n if ( typeof dataTypeExpression !== \"string\" ) {\n func = dataTypeExpression;\n dataTypeExpression = \"*\";\n }\n\n var dataType, list, placeBefore,\n dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ),\n i = 0,\n length = dataTypes.length;\n\n if ( jQuery.isFunction( func ) ) {\n // For each dataType in the dataTypeExpression\n for ( ; i < length; i++ ) {\n dataType = dataTypes[ i ];\n // We control if we're asked to add before\n // any existing element\n placeBefore = /^\\+/.test( dataType );\n if ( placeBefore ) {\n dataType = dataType.substr( 1 ) || \"*\";\n }\n list = structure[ dataType ] = structure[ dataType ] || [];\n // then we add to the structure accordingly\n list[ placeBefore ? \"unshift\" : \"push\" ]( func );\n }\n }\n };\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,\n dataType /* internal */, inspected /* internal */ ) {\n\n dataType = dataType || options.dataTypes[ 0 ];\n inspected = inspected || {};\n\n inspected[ dataType ] = true;\n\n var selection,\n list = structure[ dataType ],\n i = 0,\n length = list ? list.length : 0,\n executeOnly = ( structure === prefilters );\n\n for ( ; i < length && ( executeOnly || !selection ); i++ ) {\n selection = list[ i ]( options, originalOptions, jqXHR );\n // If we got redirected to another dataType\n // we try there if executing only and not done already\n if ( typeof selection === \"string\" ) {\n if ( !executeOnly || inspected[ selection ] ) {\n selection = undefined;\n } else {\n options.dataTypes.unshift( selection );\n selection = inspectPrefiltersOrTransports(\n structure, options, originalOptions, jqXHR, selection, inspected );\n }\n }\n }\n // If we're only executing or nothing was selected\n // we try the catchall dataType if not done already\n if ( ( executeOnly || !selection ) && !inspected[ \"*\" ] ) {\n selection = inspectPrefiltersOrTransports(\n structure, options, originalOptions, jqXHR, \"*\", inspected );\n }\n // unnecessary when only executing (prefilters)\n // but it'll be ignored by the caller in that case\n return selection;\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes #9887\nfunction ajaxExtend( target, src ) {\n var key, deep,\n flatOptions = jQuery.ajaxSettings.flatOptions || {};\n for ( key in src ) {\n if ( src[ key ] !== undefined ) {\n ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n }\n }\n if ( deep ) {\n jQuery.extend( true, target, deep );\n }\n}\n\njQuery.fn.load = function( url, params, callback ) {\n if ( typeof url !== \"string\" && _load ) {\n return _load.apply( this, arguments );\n }\n\n // Don't do a request if no elements are being requested\n if ( !this.length ) {\n return this;\n }\n\n var selector, type, response,\n self = this,\n off = url.indexOf(\" \");\n\n if ( off >= 0 ) {\n selector = url.slice( off, url.length );\n url = url.slice( 0, off );\n }\n\n // If it's a function\n if ( jQuery.isFunction( params ) ) {\n\n // We assume that it's the callback\n callback = params;\n params = undefined;\n\n // Otherwise, build a param string\n } else if ( params && typeof params === \"object\" ) {\n type = \"POST\";\n }\n\n // Request the remote document\n jQuery.ajax({\n url: url,\n\n // if \"type\" variable is undefined, then \"GET\" method will be used\n type: type,\n dataType: \"html\",\n data: params,\n complete: function( jqXHR, status ) {\n if ( callback ) {\n self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );\n }\n }\n }).done(function( responseText ) {\n\n // Save response for use in complete callback\n response = arguments;\n\n // See if a selector was specified\n self.html( selector ?\n\n // Create a dummy div to hold the results\n jQuery(\"<div>\")\n\n // inject the contents of the document in, removing the scripts\n // to avoid any 'Permission Denied' errors in IE\n .append( responseText.replace( rscript, \"\" ) )\n\n // Locate the specified elements\n .find( selector ) :\n\n // If not, just inject the full result\n responseText );\n\n });\n\n return this;\n};\n\n// Attach a bunch of functions for handling common AJAX events\njQuery.each( \"ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend\".split( \" \" ), function( i, o ){\n jQuery.fn[ o ] = function( f ){\n return this.on( o, f );\n };\n});\n\njQuery.each( [ \"get\", \"post\" ], function( i, method ) {\n jQuery[ method ] = function( url, data, callback, type ) {\n // shift arguments if data argument was omitted\n if ( jQuery.isFunction( data ) ) {\n type = type || callback;\n callback = data;\n data = undefined;\n }\n\n return jQuery.ajax({\n type: method,\n url: url,\n data: data,\n success: callback,\n dataType: type\n });\n };\n});\n\njQuery.extend({\n\n getScript: function( url, callback ) {\n return jQuery.get( url, undefined, callback, \"script\" );\n },\n\n getJSON: function( url, data, callback ) {\n return jQuery.get( url, data, callback, \"json\" );\n },\n\n // Creates a full fledged settings object into target\n // with both ajaxSettings and settings fields.\n // If target is omitted, writes into ajaxSettings.\n ajaxSetup: function( target, settings ) {\n if ( settings ) {\n // Building a settings object\n ajaxExtend( target, jQuery.ajaxSettings );\n } else {\n // Extending ajaxSettings\n settings = target;\n target = jQuery.ajaxSettings;\n }\n ajaxExtend( target, settings );\n return target;\n },\n\n ajaxSettings: {\n url: ajaxLocation,\n isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),\n global: true,\n type: \"GET\",\n contentType: \"application/x-www-form-urlencoded; charset=UTF-8\",\n processData: true,\n async: true,\n /*\n timeout: 0,\n data: null,\n dataType: null,\n username: null,\n password: null,\n cache: null,\n throws: false,\n traditional: false,\n headers: {},\n */\n\n accepts: {\n xml: \"application/xml, text/xml\",\n html: \"text/html\",\n text: \"text/plain\",\n json: \"application/json, text/javascript\",\n \"*\": allTypes\n },\n\n contents: {\n xml: /xml/,\n html: /html/,\n json: /json/\n },\n\n responseFields: {\n xml: \"responseXML\",\n text: \"responseText\"\n },\n\n // List of data converters\n // 1) key format is \"source_type destination_type\" (a single space in-between)\n // 2) the catchall symbol \"*\" can be used for source_type\n converters: {\n\n // Convert anything to text\n \"* text\": window.String,\n\n // Text to html (true = no transformation)\n \"text html\": true,\n\n // Evaluate text as a json expression\n \"text json\": jQuery.parseJSON,\n\n // Parse text as xml\n \"text xml\": jQuery.parseXML\n },\n\n // For options that shouldn't be deep extended:\n // you can add your own custom options here if\n // and when you create one that shouldn't be\n // deep extended (see ajaxExtend)\n flatOptions: {\n context: true,\n url: true\n }\n },\n\n ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n ajaxTransport: addToPrefiltersOrTransports( transports ),\n\n // Main method\n ajax: function( url, options ) {\n\n // If url is an object, simulate pre-1.5 signature\n if ( typeof url === \"object\" ) {\n options = url;\n url = undefined;\n }\n\n // Force options to be an object\n options = options || {};\n\n var // ifModified key\n ifModifiedKey,\n // Response headers\n responseHeadersString,\n responseHeaders,\n // transport\n transport,\n // timeout handle\n timeoutTimer,\n // Cross-domain detection vars\n parts,\n // To know if global events are to be dispatched\n fireGlobals,\n // Loop variable\n i,\n // Create the final options object\n s = jQuery.ajaxSetup( {}, options ),\n // Callbacks context\n callbackContext = s.context || s,\n // Context for global events\n // It's the callbackContext if one was provided in the options\n // and if it's a DOM node or a jQuery collection\n globalEventContext = callbackContext !== s &&\n ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?\n jQuery( callbackContext ) : jQuery.event,\n // Deferreds\n deferred = jQuery.Deferred(),\n completeDeferred = jQuery.Callbacks( \"once memory\" ),\n // Status-dependent callbacks\n statusCode = s.statusCode || {},\n // Headers (they are sent all at once)\n requestHeaders = {},\n requestHeadersNames = {},\n // The jqXHR state\n state = 0,\n // Default abort message\n strAbort = \"canceled\",\n // Fake xhr\n jqXHR = {\n\n readyState: 0,\n\n // Caches the header\n setRequestHeader: function( name, value ) {\n if ( !state ) {\n var lname = name.toLowerCase();\n name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;\n requestHeaders[ name ] = value;\n }\n return this;\n },\n\n // Raw string\n getAllResponseHeaders: function() {\n return state === 2 ? responseHeadersString : null;\n },\n\n // Builds headers hashtable if needed\n getResponseHeader: function( key ) {\n var match;\n if ( state === 2 ) {\n if ( !responseHeaders ) {\n responseHeaders = {};\n while( ( match = rheaders.exec( responseHeadersString ) ) ) {\n responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];\n }\n }\n match = responseHeaders[ key.toLowerCase() ];\n }\n return match === undefined ? null : match;\n },\n\n // Overrides response content-type header\n overrideMimeType: function( type ) {\n if ( !state ) {\n s.mimeType = type;\n }\n return this;\n },\n\n // Cancel the request\n abort: function( statusText ) {\n statusText = statusText || strAbort;\n if ( transport ) {\n transport.abort( statusText );\n }\n done( 0, statusText );\n return this;\n }\n };\n\n // Callback for when everything is done\n // It is defined here because jslint complains if it is declared\n // at the end of the function (which would be more logical and readable)\n function done( status, nativeStatusText, responses, headers ) {\n var isSuccess, success, error, response, modified,\n statusText = nativeStatusText;\n\n // Called once\n if ( state === 2 ) {\n return;\n }\n\n // State is \"done\" now\n state = 2;\n\n // Clear timeout if it exists\n if ( timeoutTimer ) {\n clearTimeout( timeoutTimer );\n }\n\n // Dereference transport for early garbage collection\n // (no matter how long the jqXHR object will be used)\n transport = undefined;\n\n // Cache response headers\n responseHeadersString = headers || \"\";\n\n // Set readyState\n jqXHR.readyState = status > 0 ? 4 : 0;\n\n // Get response data\n if ( responses ) {\n response = ajaxHandleResponses( s, jqXHR, responses );\n }\n\n // If successful, handle type chaining\n if ( status >= 200 && status < 300 || status === 304 ) {\n\n // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n if ( s.ifModified ) {\n\n modified = jqXHR.getResponseHeader(\"Last-Modified\");\n if ( modified ) {\n jQuery.lastModified[ ifModifiedKey ] = modified;\n }\n modified = jqXHR.getResponseHeader(\"Etag\");\n if ( modified ) {\n jQuery.etag[ ifModifiedKey ] = modified;\n }\n }\n\n // If not modified\n if ( status === 304 ) {\n\n statusText = \"notmodified\";\n isSuccess = true;\n\n // If we have data\n } else {\n\n isSuccess = ajaxConvert( s, response );\n statusText = isSuccess.state;\n success = isSuccess.data;\n error = isSuccess.error;\n isSuccess = !error;\n }\n } else {\n // We extract error from statusText\n // then normalize statusText and status for non-aborts\n error = statusText;\n if ( !statusText || status ) {\n statusText = \"error\";\n if ( status < 0 ) {\n status = 0;\n }\n }\n }\n\n // Set data for the fake xhr object\n jqXHR.status = status;\n jqXHR.statusText = ( nativeStatusText || statusText ) + \"\";\n\n // Success/Error\n if ( isSuccess ) {\n deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n } else {\n deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n }\n\n // Status-dependent callbacks\n jqXHR.statusCode( statusCode );\n statusCode = undefined;\n\n if ( fireGlobals ) {\n globalEventContext.trigger( \"ajax\" + ( isSuccess ? \"Success\" : \"Error\" ),\n [ jqXHR, s, isSuccess ? success : error ] );\n }\n\n // Complete\n completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n if ( fireGlobals ) {\n globalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n // Handle the global AJAX counter\n if ( !( --jQuery.active ) ) {\n jQuery.event.trigger( \"ajaxStop\" );\n }\n }\n }\n\n // Attach deferreds\n deferred.promise( jqXHR );\n jqXHR.success = jqXHR.done;\n jqXHR.error = jqXHR.fail;\n jqXHR.complete = completeDeferred.add;\n\n // Status-dependent callbacks\n jqXHR.statusCode = function( map ) {\n if ( map ) {\n var tmp;\n if ( state < 2 ) {\n for ( tmp in map ) {\n statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];\n }\n } else {\n tmp = map[ jqXHR.status ];\n jqXHR.always( tmp );\n }\n }\n return this;\n };\n\n // Remove hash character (#7531: and string promotion)\n // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)\n // We also use the url parameter if available\n s.url = ( ( url || s.url ) + \"\" ).replace( rhash, \"\" ).replace( rprotocol, ajaxLocParts[ 1 ] + \"//\" );\n\n // Extract dataTypes list\n s.dataTypes = jQuery.trim( s.dataType || \"*\" ).toLowerCase().split( core_rspace );\n\n // A cross-domain request is in order when we have a protocol:host:port mismatch\n if ( s.crossDomain == null ) {\n parts = rurl.exec( s.url.toLowerCase() );\n s.crossDomain = !!( parts &&\n ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||\n ( parts[ 3 ] || ( parts[ 1 ] === \"http:\" ? 80 : 443 ) ) !=\n ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === \"http:\" ? 80 : 443 ) ) )\n );\n }\n\n // Convert data if not already a string\n if ( s.data && s.processData && typeof s.data !== \"string\" ) {\n s.data = jQuery.param( s.data, s.traditional );\n }\n\n // Apply prefilters\n inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n // If request was aborted inside a prefilter, stop there\n if ( state === 2 ) {\n return jqXHR;\n }\n\n // We can fire global events as of now if asked to\n fireGlobals = s.global;\n\n // Uppercase the type\n s.type = s.type.toUpperCase();\n\n // Determine if request has content\n s.hasContent = !rnoContent.test( s.type );\n\n // Watch for a new set of requests\n if ( fireGlobals && jQuery.active++ === 0 ) {\n jQuery.event.trigger( \"ajaxStart\" );\n }\n\n // More options handling for requests with no content\n if ( !s.hasContent ) {\n\n // If data is available, append data to url\n if ( s.data ) {\n s.url += ( rquery.test( s.url ) ? \"&\" : \"?\" ) + s.data;\n // #9682: remove data so that it's not used in an eventual retry\n delete s.data;\n }\n\n // Get ifModifiedKey before adding the anti-cache parameter\n ifModifiedKey = s.url;\n\n // Add anti-cache in url if needed\n if ( s.cache === false ) {\n\n var ts = jQuery.now(),\n // try replacing _= if it is there\n ret = s.url.replace( rts, \"$1_=\" + ts );\n\n // if nothing was replaced, add timestamp to the end\n s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? \"&\" : \"?\" ) + \"_=\" + ts : \"\" );\n }\n }\n\n // Set the correct header, if data is being sent\n if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n jqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n }\n\n // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n if ( s.ifModified ) {\n ifModifiedKey = ifModifiedKey || s.url;\n if ( jQuery.lastModified[ ifModifiedKey ] ) {\n jqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ ifModifiedKey ] );\n }\n if ( jQuery.etag[ ifModifiedKey ] ) {\n jqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ ifModifiedKey ] );\n }\n }\n\n // Set the Accepts header for the server, depending on the dataType\n jqXHR.setRequestHeader(\n \"Accept\",\n s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?\n s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n s.accepts[ \"*\" ]\n );\n\n // Check for headers option\n for ( i in s.headers ) {\n jqXHR.setRequestHeader( i, s.headers[ i ] );\n }\n\n // Allow custom headers/mimetypes and early abort\n if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {\n // Abort if not done already and return\n return jqXHR.abort();\n\n }\n\n // aborting is no longer a cancellation\n strAbort = \"abort\";\n\n // Install callbacks on deferreds\n for ( i in { success: 1, error: 1, complete: 1 } ) {\n jqXHR[ i ]( s[ i ] );\n }\n\n // Get transport\n transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n // If no transport, we auto-abort\n if ( !transport ) {\n done( -1, \"No Transport\" );\n } else {\n jqXHR.readyState = 1;\n // Send global event\n if ( fireGlobals ) {\n globalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n }\n // Timeout\n if ( s.async && s.timeout > 0 ) {\n timeoutTimer = setTimeout( function(){\n jqXHR.abort( \"timeout\" );\n }, s.timeout );\n }\n\n try {\n state = 1;\n transport.send( requestHeaders, done );\n } catch (e) {\n // Propagate exception as error if not done\n if ( state < 2 ) {\n done( -1, e );\n // Simply rethrow otherwise\n } else {\n throw e;\n }\n }\n }\n\n return jqXHR;\n },\n\n // Counter for holding the number of active queries\n active: 0,\n\n // Last-Modified header cache for next request\n lastModified: {},\n etag: {}\n\n});\n\n/* Handles responses to an ajax request:\n * - sets all responseXXX fields accordingly\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n var ct, type, finalDataType, firstDataType,\n contents = s.contents,\n dataTypes = s.dataTypes,\n responseFields = s.responseFields;\n\n // Fill responseXXX fields\n for ( type in responseFields ) {\n if ( type in responses ) {\n jqXHR[ responseFields[type] ] = responses[ type ];\n }\n }\n\n // Remove auto dataType and get content-type in the process\n while( dataTypes[ 0 ] === \"*\" ) {\n dataTypes.shift();\n if ( ct === undefined ) {\n ct = s.mimeType || jqXHR.getResponseHeader( \"content-type\" );\n }\n }\n\n // Check if we're dealing with a known content-type\n if ( ct ) {\n for ( type in contents ) {\n if ( contents[ type ] && contents[ type ].test( ct ) ) {\n dataTypes.unshift( type );\n break;\n }\n }\n }\n\n // Check to see if we have a response for the expected dataType\n if ( dataTypes[ 0 ] in responses ) {\n finalDataType = dataTypes[ 0 ];\n } else {\n // Try convertible dataTypes\n for ( type in responses ) {\n if ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[0] ] ) {\n finalDataType = type;\n break;\n }\n if ( !firstDataType ) {\n firstDataType = type;\n }\n }\n // Or just use first one\n finalDataType = finalDataType || firstDataType;\n }\n\n // If we found a dataType\n // We add the dataType to the list if needed\n // and return the corresponding response\n if ( finalDataType ) {\n if ( finalDataType !== dataTypes[ 0 ] ) {\n dataTypes.unshift( finalDataType );\n }\n return responses[ finalDataType ];\n }\n}\n\n// Chain conversions given the request and the original response\nfunction ajaxConvert( s, response ) {\n\n var conv, conv2, current, tmp,\n // Work with a copy of dataTypes in case we need to modify it for conversion\n dataTypes = s.dataTypes.slice(),\n prev = dataTypes[ 0 ],\n converters = {},\n i = 0;\n\n // Apply the dataFilter if provided\n if ( s.dataFilter ) {\n response = s.dataFilter( response, s.dataType );\n }\n\n // Create converters map with lowercased keys\n if ( dataTypes[ 1 ] ) {\n for ( conv in s.converters ) {\n converters[ conv.toLowerCase() ] = s.converters[ conv ];\n }\n }\n\n // Convert to each sequential dataType, tolerating list modification\n for ( ; (current = dataTypes[++i]); ) {\n\n // There's only work to do if current dataType is non-auto\n if ( current !== \"*\" ) {\n\n // Convert response if prev dataType is non-auto and differs from current\n if ( prev !== \"*\" && prev !== current ) {\n\n // Seek a direct converter\n conv = converters[ prev + \" \" + current ] || converters[ \"* \" + current ];\n\n // If none found, seek a pair\n if ( !conv ) {\n for ( conv2 in converters ) {\n\n // If conv2 outputs current\n tmp = conv2.split(\" \");\n if ( tmp[ 1 ] === current ) {\n\n // If prev can be converted to accepted input\n conv = converters[ prev + \" \" + tmp[ 0 ] ] ||\n converters[ \"* \" + tmp[ 0 ] ];\n if ( conv ) {\n // Condense equivalence converters\n if ( conv === true ) {\n conv = converters[ conv2 ];\n\n // Otherwise, insert the intermediate dataType\n } else if ( converters[ conv2 ] !== true ) {\n current = tmp[ 0 ];\n dataTypes.splice( i--, 0, current );\n }\n\n break;\n }\n }\n }\n }\n\n // Apply converter (if not an equivalence)\n if ( conv !== true ) {\n\n // Unless errors are allowed to bubble, catch and return them\n if ( conv && s[\"throws\"] ) {\n response = conv( response );\n } else {\n try {\n response = conv( response );\n } catch ( e ) {\n return { state: \"parsererror\", error: conv ? e : \"No conversion from \" + prev + \" to \" + current };\n }\n }\n }\n }\n\n // Update prev for next iteration\n prev = current;\n }\n }\n\n return { state: \"success\", data: response };\n}\nvar oldCallbacks = [],\n rquestion = /\\?/,\n rjsonp = /(=)\\?(?=&|$)|\\?\\?/,\n nonce = jQuery.now();\n\n// Default jsonp settings\njQuery.ajaxSetup({\n jsonp: \"callback\",\n jsonpCallback: function() {\n var callback = oldCallbacks.pop() || ( jQuery.expando + \"_\" + ( nonce++ ) );\n this[ callback ] = true;\n return callback;\n }\n});\n\n// Detect, normalize options and install callbacks for jsonp requests\njQuery.ajaxPrefilter( \"json jsonp\", function( s, originalSettings, jqXHR ) {\n\n var callbackName, overwritten, responseContainer,\n data = s.data,\n url = s.url,\n hasCallback = s.jsonp !== false,\n replaceInUrl = hasCallback && rjsonp.test( url ),\n replaceInData = hasCallback && !replaceInUrl && typeof data === \"string\" &&\n !( s.contentType || \"\" ).indexOf(\"application/x-www-form-urlencoded\") &&\n rjsonp.test( data );\n\n // Handle iff the expected data type is \"jsonp\" or we have a parameter to set\n if ( s.dataTypes[ 0 ] === \"jsonp\" || replaceInUrl || replaceInData ) {\n\n // Get callback name, remembering preexisting value associated with it\n callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?\n s.jsonpCallback() :\n s.jsonpCallback;\n overwritten = window[ callbackName ];\n\n // Insert callback into url or form data\n if ( replaceInUrl ) {\n s.url = url.replace( rjsonp, \"$1\" + callbackName );\n } else if ( replaceInData ) {\n s.data = data.replace( rjsonp, \"$1\" + callbackName );\n } else if ( hasCallback ) {\n s.url += ( rquestion.test( url ) ? \"&\" : \"?\" ) + s.jsonp + \"=\" + callbackName;\n }\n\n // Use data converter to retrieve json after script execution\n s.converters[\"script json\"] = function() {\n if ( !responseContainer ) {\n jQuery.error( callbackName + \" was not called\" );\n }\n return responseContainer[ 0 ];\n };\n\n // force json dataType\n s.dataTypes[ 0 ] = \"json\";\n\n // Install callback\n window[ callbackName ] = function() {\n responseContainer = arguments;\n };\n\n // Clean-up function (fires after converters)\n jqXHR.always(function() {\n // Restore preexisting value\n window[ callbackName ] = overwritten;\n\n // Save back as free\n if ( s[ callbackName ] ) {\n // make sure that re-using the options doesn't screw things around\n s.jsonpCallback = originalSettings.jsonpCallback;\n\n // save the callback name for future use\n oldCallbacks.push( callbackName );\n }\n\n // Call if it was a function and we have a response\n if ( responseContainer && jQuery.isFunction( overwritten ) ) {\n overwritten( responseContainer[ 0 ] );\n }\n\n responseContainer = overwritten = undefined;\n });\n\n // Delegate to script\n return \"script\";\n }\n});\n// Install script dataType\njQuery.ajaxSetup({\n accepts: {\n script: \"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"\n },\n contents: {\n script: /javascript|ecmascript/\n },\n converters: {\n \"text script\": function( text ) {\n jQuery.globalEval( text );\n return text;\n }\n }\n});\n\n// Handle cache's special case and global\njQuery.ajaxPrefilter( \"script\", function( s ) {\n if ( s.cache === undefined ) {\n s.cache = false;\n }\n if ( s.crossDomain ) {\n s.type = \"GET\";\n s.global = false;\n }\n});\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function(s) {\n\n // This transport only deals with cross domain requests\n if ( s.crossDomain ) {\n\n var script,\n head = document.head || document.getElementsByTagName( \"head\" )[0] || document.documentElement;\n\n return {\n\n send: function( _, callback ) {\n\n script = document.createElement( \"script\" );\n\n script.async = \"async\";\n\n if ( s.scriptCharset ) {\n script.charset = s.scriptCharset;\n }\n\n script.src = s.url;\n\n // Attach handlers for all browsers\n script.onload = script.onreadystatechange = function( _, isAbort ) {\n\n if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {\n\n // Handle memory leak in IE\n script.onload = script.onreadystatechange = null;\n\n // Remove the script\n if ( head && script.parentNode ) {\n head.removeChild( script );\n }\n\n // Dereference the script\n script = undefined;\n\n // Callback if not abort\n if ( !isAbort ) {\n callback( 200, \"success\" );\n }\n }\n };\n // Use insertBefore instead of appendChild to circumvent an IE6 bug.\n // This arises when a base node is used (#2709 and #4378).\n head.insertBefore( script, head.firstChild );\n },\n\n abort: function() {\n if ( script ) {\n script.onload( 0, 1 );\n }\n }\n };\n }\n});\nvar xhrCallbacks,\n // #5280: Internet Explorer will keep connections alive if we don't abort on unload\n xhrOnUnloadAbort = window.ActiveXObject ? function() {\n // Abort all pending requests\n for ( var key in xhrCallbacks ) {\n xhrCallbacks[ key ]( 0, 1 );\n }\n } : false,\n xhrId = 0;\n\n// Functions to create xhrs\nfunction createStandardXHR() {\n try {\n return new window.XMLHttpRequest();\n } catch( e ) {}\n}\n\nfunction createActiveXHR() {\n try {\n return new window.ActiveXObject( \"Microsoft.XMLHTTP\" );\n } catch( e ) {}\n}\n\n// Create the request object\n// (This is still attached to ajaxSettings for backward compatibility)\njQuery.ajaxSettings.xhr = window.ActiveXObject ?\n /* Microsoft failed to properly\n * implement the XMLHttpRequest in IE7 (can't request local files),\n * so we use the ActiveXObject when it is available\n * Additionally XMLHttpRequest can be disabled in IE7/IE8 so\n * we need a fallback.\n */\n function() {\n return !this.isLocal && createStandardXHR() || createActiveXHR();\n } :\n // For all other browsers, use the standard XMLHttpRequest object\n createStandardXHR;\n\n// Determine support properties\n(function( xhr ) {\n jQuery.extend( jQuery.support, {\n ajax: !!xhr,\n cors: !!xhr && ( \"withCredentials\" in xhr )\n });\n})( jQuery.ajaxSettings.xhr() );\n\n// Create transport if the browser can provide an xhr\nif ( jQuery.support.ajax ) {\n\n jQuery.ajaxTransport(function( s ) {\n // Cross domain only allowed if supported through XMLHttpRequest\n if ( !s.crossDomain || jQuery.support.cors ) {\n\n var callback;\n\n return {\n send: function( headers, complete ) {\n\n // Get a new xhr\n var handle, i,\n xhr = s.xhr();\n\n // Open the socket\n // Passing null username, generates a login popup on Opera (#2865)\n if ( s.username ) {\n xhr.open( s.type, s.url, s.async, s.username, s.password );\n } else {\n xhr.open( s.type, s.url, s.async );\n }\n\n // Apply custom fields if provided\n if ( s.xhrFields ) {\n for ( i in s.xhrFields ) {\n xhr[ i ] = s.xhrFields[ i ];\n }\n }\n\n // Override mime type if needed\n if ( s.mimeType && xhr.overrideMimeType ) {\n xhr.overrideMimeType( s.mimeType );\n }\n\n // X-Requested-With header\n // For cross-domain requests, seeing as conditions for a preflight are\n // akin to a jigsaw puzzle, we simply never set it to be sure.\n // (it can always be set on a per-request basis or even using ajaxSetup)\n // For same-domain requests, won't change header if already provided.\n if ( !s.crossDomain && !headers[\"X-Requested-With\"] ) {\n headers[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n }\n\n // Need an extra try/catch for cross domain requests in Firefox 3\n try {\n for ( i in headers ) {\n xhr.setRequestHeader( i, headers[ i ] );\n }\n } catch( _ ) {}\n\n // Do send the request\n // This may raise an exception which is actually\n // handled in jQuery.ajax (so no try/catch here)\n xhr.send( ( s.hasContent && s.data ) || null );\n\n // Listener\n callback = function( _, isAbort ) {\n\n var status,\n statusText,\n responseHeaders,\n responses,\n xml;\n\n // Firefox throws exceptions when accessing properties\n // of an xhr when a network error occurred\n // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)\n try {\n\n // Was never called and is aborted or complete\n if ( callback && ( isAbort || xhr.readyState === 4 ) ) {\n\n // Only called once\n callback = undefined;\n\n // Do not keep as active anymore\n if ( handle ) {\n xhr.onreadystatechange = jQuery.noop;\n if ( xhrOnUnloadAbort ) {\n delete xhrCallbacks[ handle ];\n }\n }\n\n // If it's an abort\n if ( isAbort ) {\n // Abort it manually if needed\n if ( xhr.readyState !== 4 ) {\n xhr.abort();\n }\n } else {\n status = xhr.status;\n responseHeaders = xhr.getAllResponseHeaders();\n responses = {};\n xml = xhr.responseXML;\n\n // Construct response list\n if ( xml && xml.documentElement /* #4958 */ ) {\n responses.xml = xml;\n }\n\n // When requesting binary data, IE6-9 will throw an exception\n // on any attempt to access responseText (#11426)\n try {\n responses.text = xhr.responseText;\n } catch( e ) {\n }\n\n // Firefox throws an exception when accessing\n // statusText for faulty cross-domain requests\n try {\n statusText = xhr.statusText;\n } catch( e ) {\n // We normalize with Webkit giving an empty statusText\n statusText = \"\";\n }\n\n // Filter status for non standard behaviors\n\n // If the request is local and we have data: assume a success\n // (success with no data won't get notified, that's the best we\n // can do given current implementations)\n if ( !status && s.isLocal && !s.crossDomain ) {\n status = responses.text ? 200 : 404;\n // IE - #1450: sometimes returns 1223 when it should be 204\n } else if ( status === 1223 ) {\n status = 204;\n }\n }\n }\n } catch( firefoxAccessException ) {\n if ( !isAbort ) {\n complete( -1, firefoxAccessException );\n }\n }\n\n // Call complete if needed\n if ( responses ) {\n complete( status, statusText, responses, responseHeaders );\n }\n };\n\n if ( !s.async ) {\n // if we're in sync mode we fire the callback\n callback();\n } else if ( xhr.readyState === 4 ) {\n // (IE6 & IE7) if it's in cache and has been\n // retrieved directly we need to fire the callback\n setTimeout( callback, 0 );\n } else {\n handle = ++xhrId;\n if ( xhrOnUnloadAbort ) {\n // Create the active xhrs callbacks list if needed\n // and attach the unload handler\n if ( !xhrCallbacks ) {\n xhrCallbacks = {};\n jQuery( window ).unload( xhrOnUnloadAbort );\n }\n // Add to list of active xhrs callbacks\n xhrCallbacks[ handle ] = callback;\n }\n xhr.onreadystatechange = callback;\n }\n },\n\n abort: function() {\n if ( callback ) {\n callback(0,1);\n }\n }\n };\n }\n });\n}\nvar fxNow, timerId,\n rfxtypes = /^(?:toggle|show|hide)$/,\n rfxnum = new RegExp( \"^(?:([-+])=|)(\" + core_pnum + \")([a-z%]*)$\", \"i\" ),\n rrun = /queueHooks$/,\n animationPrefilters = [ defaultPrefilter ],\n tweeners = {\n \"*\": [function( prop, value ) {\n var end, unit,\n tween = this.createTween( prop, value ),\n parts = rfxnum.exec( value ),\n target = tween.cur(),\n start = +target || 0,\n scale = 1,\n maxIterations = 20;\n\n if ( parts ) {\n end = +parts[2];\n unit = parts[3] || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n\n // We need to compute starting value\n if ( unit !== \"px\" && start ) {\n // Iteratively approximate from a nonzero starting point\n // Prefer the current property, because this process will be trivial if it uses the same units\n // Fallback to end or a simple constant\n start = jQuery.css( tween.elem, prop, true ) || end || 1;\n\n do {\n // If previous iteration zeroed out, double until we get *something*\n // Use a string for doubling factor so we don't accidentally see scale as unchanged below\n scale = scale || \".5\";\n\n // Adjust and apply\n start = start / scale;\n jQuery.style( tween.elem, prop, start + unit );\n\n // Update scale, tolerating zero or NaN from tween.cur()\n // And breaking the loop if scale is unchanged or perfect, or if we've just had enough\n } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );\n }\n\n tween.unit = unit;\n tween.start = start;\n // If a +=/-= token was provided, we're doing a relative animation\n tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;\n }\n return tween;\n }]\n };\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n setTimeout(function() {\n fxNow = undefined;\n }, 0 );\n return ( fxNow = jQuery.now() );\n}\n\nfunction createTweens( animation, props ) {\n jQuery.each( props, function( prop, value ) {\n var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ \"*\" ] ),\n index = 0,\n length = collection.length;\n for ( ; index < length; index++ ) {\n if ( collection[ index ].call( animation, prop, value ) ) {\n\n // we're done with this property\n return;\n }\n }\n });\n}\n\nfunction Animation( elem, properties, options ) {\n var result,\n index = 0,\n tweenerIndex = 0,\n length = animationPrefilters.length,\n deferred = jQuery.Deferred().always( function() {\n // don't match elem in the :animated selector\n delete tick.elem;\n }),\n tick = function() {\n var currentTime = fxNow || createFxNow(),\n remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),\n // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)\n temp = remaining / animation.duration || 0,\n percent = 1 - temp,\n index = 0,\n length = animation.tweens.length;\n\n for ( ; index < length ; index++ ) {\n animation.tweens[ index ].run( percent );\n }\n\n deferred.notifyWith( elem, [ animation, percent, remaining ]);\n\n if ( percent < 1 && length ) {\n return remaining;\n } else {\n deferred.resolveWith( elem, [ animation ] );\n return false;\n }\n },\n animation = deferred.promise({\n elem: elem,\n props: jQuery.extend( {}, properties ),\n opts: jQuery.extend( true, { specialEasing: {} }, options ),\n originalProperties: properties,\n originalOptions: options,\n startTime: fxNow || createFxNow(),\n duration: options.duration,\n tweens: [],\n createTween: function( prop, end, easing ) {\n var tween = jQuery.Tween( elem, animation.opts, prop, end,\n animation.opts.specialEasing[ prop ] || animation.opts.easing );\n animation.tweens.push( tween );\n return tween;\n },\n stop: function( gotoEnd ) {\n var index = 0,\n // if we are going to the end, we want to run all the tweens\n // otherwise we skip this part\n length = gotoEnd ? animation.tweens.length : 0;\n\n for ( ; index < length ; index++ ) {\n animation.tweens[ index ].run( 1 );\n }\n\n // resolve when we played the last frame\n // otherwise, reject\n if ( gotoEnd ) {\n deferred.resolveWith( elem, [ animation, gotoEnd ] );\n } else {\n deferred.rejectWith( elem, [ animation, gotoEnd ] );\n }\n return this;\n }\n }),\n props = animation.props;\n\n propFilter( props, animation.opts.specialEasing );\n\n for ( ; index < length ; index++ ) {\n result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );\n if ( result ) {\n return result;\n }\n }\n\n createTweens( animation, props );\n\n if ( jQuery.isFunction( animation.opts.start ) ) {\n animation.opts.start.call( elem, animation );\n }\n\n jQuery.fx.timer(\n jQuery.extend( tick, {\n anim: animation,\n queue: animation.opts.queue,\n elem: elem\n })\n );\n\n // attach callbacks from options\n return animation.progress( animation.opts.progress )\n .done( animation.opts.done, animation.opts.complete )\n .fail( animation.opts.fail )\n .always( animation.opts.always );\n}\n\nfunction propFilter( props, specialEasing ) {\n var index, name, easing, value, hooks;\n\n // camelCase, specialEasing and expand cssHook pass\n for ( index in props ) {\n name = jQuery.camelCase( index );\n easing = specialEasing[ name ];\n value = props[ index ];\n if ( jQuery.isArray( value ) ) {\n easing = value[ 1 ];\n value = props[ index ] = value[ 0 ];\n }\n\n if ( index !== name ) {\n props[ name ] = value;\n delete props[ index ];\n }\n\n hooks = jQuery.cssHooks[ name ];\n if ( hooks && \"expand\" in hooks ) {\n value = hooks.expand( value );\n delete props[ name ];\n\n // not quite $.extend, this wont overwrite keys already present.\n // also - reusing 'index' from above because we have the correct \"name\"\n for ( index in value ) {\n if ( !( index in props ) ) {\n props[ index ] = value[ index ];\n specialEasing[ index ] = easing;\n }\n }\n } else {\n specialEasing[ name ] = easing;\n }\n }\n}\n\njQuery.Animation = jQuery.extend( Animation, {\n\n tweener: function( props, callback ) {\n if ( jQuery.isFunction( props ) ) {\n callback = props;\n props = [ \"*\" ];\n } else {\n props = props.split(\" \");\n }\n\n var prop,\n index = 0,\n length = props.length;\n\n for ( ; index < length ; index++ ) {\n prop = props[ index ];\n tweeners[ prop ] = tweeners[ prop ] || [];\n tweeners[ prop ].unshift( callback );\n }\n },\n\n prefilter: function( callback, prepend ) {\n if ( prepend ) {\n animationPrefilters.unshift( callback );\n } else {\n animationPrefilters.push( callback );\n }\n }\n});\n\nfunction defaultPrefilter( elem, props, opts ) {\n var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire,\n anim = this,\n style = elem.style,\n orig = {},\n handled = [],\n hidden = elem.nodeType && isHidden( elem );\n\n // handle queue: false promises\n if ( !opts.queue ) {\n hooks = jQuery._queueHooks( elem, \"fx\" );\n if ( hooks.unqueued == null ) {\n hooks.unqueued = 0;\n oldfire = hooks.empty.fire;\n hooks.empty.fire = function() {\n if ( !hooks.unqueued ) {\n oldfire();\n }\n };\n }\n hooks.unqueued++;\n\n anim.always(function() {\n // doing this makes sure that the complete handler will be called\n // before this completes\n anim.always(function() {\n hooks.unqueued--;\n if ( !jQuery.queue( elem, \"fx\" ).length ) {\n hooks.empty.fire();\n }\n });\n });\n }\n\n // height/width overflow pass\n if ( elem.nodeType === 1 && ( \"height\" in props || \"width\" in props ) ) {\n // Make sure that nothing sneaks out\n // Record all 3 overflow attributes because IE does not\n // change the overflow attribute when overflowX and\n // overflowY are set to the same value\n opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];\n\n // Set display property to inline-block for height/width\n // animations on inline elements that are having width/height animated\n if ( jQuery.css( elem, \"display\" ) === \"inline\" &&\n jQuery.css( elem, \"float\" ) === \"none\" ) {\n\n // inline-level elements accept inline-block;\n // block-level elements need to be inline with layout\n if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === \"inline\" ) {\n style.display = \"inline-block\";\n\n } else {\n style.zoom = 1;\n }\n }\n }\n\n if ( opts.overflow ) {\n style.overflow = \"hidden\";\n if ( !jQuery.support.shrinkWrapBlocks ) {\n anim.done(function() {\n style.overflow = opts.overflow[ 0 ];\n style.overflowX = opts.overflow[ 1 ];\n style.overflowY = opts.overflow[ 2 ];\n });\n }\n }\n\n\n // show/hide pass\n for ( index in props ) {\n value = props[ index ];\n if ( rfxtypes.exec( value ) ) {\n delete props[ index ];\n toggle = toggle || value === \"toggle\";\n if ( value === ( hidden ? \"hide\" : \"show\" ) ) {\n continue;\n }\n handled.push( index );\n }\n }\n\n length = handled.length;\n if ( length ) {\n dataShow = jQuery._data( elem, \"fxshow\" ) || jQuery._data( elem, \"fxshow\", {} );\n if ( \"hidden\" in dataShow ) {\n hidden = dataShow.hidden;\n }\n\n // store state if its toggle - enables .stop().toggle() to \"reverse\"\n if ( toggle ) {\n dataShow.hidden = !hidden;\n }\n if ( hidden ) {\n jQuery( elem ).show();\n } else {\n anim.done(function() {\n jQuery( elem ).hide();\n });\n }\n anim.done(function() {\n var prop;\n jQuery.removeData( elem, \"fxshow\", true );\n for ( prop in orig ) {\n jQuery.style( elem, prop, orig[ prop ] );\n }\n });\n for ( index = 0 ; index < length ; index++ ) {\n prop = handled[ index ];\n tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );\n orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );\n\n if ( !( prop in dataShow ) ) {\n dataShow[ prop ] = tween.start;\n if ( hidden ) {\n tween.end = tween.start;\n tween.start = prop === \"width\" || prop === \"height\" ? 1 : 0;\n }\n }\n }\n }\n}\n\nfunction Tween( elem, options, prop, end, easing ) {\n return new Tween.prototype.init( elem, options, prop, end, easing );\n}\njQuery.Tween = Tween;\n\nTween.prototype = {\n constructor: Tween,\n init: function( elem, options, prop, end, easing, unit ) {\n this.elem = elem;\n this.prop = prop;\n this.easing = easing || \"swing\";\n this.options = options;\n this.start = this.now = this.cur();\n this.end = end;\n this.unit = unit || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n },\n cur: function() {\n var hooks = Tween.propHooks[ this.prop ];\n\n return hooks && hooks.get ?\n hooks.get( this ) :\n Tween.propHooks._default.get( this );\n },\n run: function( percent ) {\n var eased,\n hooks = Tween.propHooks[ this.prop ];\n\n if ( this.options.duration ) {\n this.pos = eased = jQuery.easing[ this.easing ](\n percent, this.options.duration * percent, 0, 1, this.options.duration\n );\n } else {\n this.pos = eased = percent;\n }\n this.now = ( this.end - this.start ) * eased + this.start;\n\n if ( this.options.step ) {\n this.options.step.call( this.elem, this.now, this );\n }\n\n if ( hooks && hooks.set ) {\n hooks.set( this );\n } else {\n Tween.propHooks._default.set( this );\n }\n return this;\n }\n};\n\nTween.prototype.init.prototype = Tween.prototype;\n\nTween.propHooks = {\n _default: {\n get: function( tween ) {\n var result;\n\n if ( tween.elem[ tween.prop ] != null &&\n (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {\n return tween.elem[ tween.prop ];\n }\n\n // passing any value as a 4th parameter to .css will automatically\n // attempt a parseFloat and fallback to a string if the parse fails\n // so, simple values such as \"10px\" are parsed to Float.\n // complex values such as \"rotate(1rad)\" are returned as is.\n result = jQuery.css( tween.elem, tween.prop, false, \"\" );\n // Empty strings, null, undefined and \"auto\" are converted to 0.\n return !result || result === \"auto\" ? 0 : result;\n },\n set: function( tween ) {\n // use step hook for back compat - use cssHook if its there - use .style if its\n // available and use plain properties where available\n if ( jQuery.fx.step[ tween.prop ] ) {\n jQuery.fx.step[ tween.prop ]( tween );\n } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {\n jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );\n } else {\n tween.elem[ tween.prop ] = tween.now;\n }\n }\n }\n};\n\n// Remove in 2.0 - this supports IE8's panic based approach\n// to setting things on disconnected nodes\n\nTween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {\n set: function( tween ) {\n if ( tween.elem.nodeType && tween.elem.parentNode ) {\n tween.elem[ tween.prop ] = tween.now;\n }\n }\n};\n\njQuery.each([ \"toggle\", \"show\", \"hide\" ], function( i, name ) {\n var cssFn = jQuery.fn[ name ];\n jQuery.fn[ name ] = function( speed, easing, callback ) {\n return speed == null || typeof speed === \"boolean\" ||\n // special check for .toggle( handler, handler, ... )\n ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ?\n cssFn.apply( this, arguments ) :\n this.animate( genFx( name, true ), speed, easing, callback );\n };\n});\n\njQuery.fn.extend({\n fadeTo: function( speed, to, easing, callback ) {\n\n // show any hidden elements after setting opacity to 0\n return this.filter( isHidden ).css( \"opacity\", 0 ).show()\n\n // animate to the value specified\n .end().animate({ opacity: to }, speed, easing, callback );\n },\n animate: function( prop, speed, easing, callback ) {\n var empty = jQuery.isEmptyObject( prop ),\n optall = jQuery.speed( speed, easing, callback ),\n doAnimation = function() {\n // Operate on a copy of prop so per-property easing won't be lost\n var anim = Animation( this, jQuery.extend( {}, prop ), optall );\n\n // Empty animations resolve immediately\n if ( empty ) {\n anim.stop( true );\n }\n };\n\n return empty || optall.queue === false ?\n this.each( doAnimation ) :\n this.queue( optall.queue, doAnimation );\n },\n stop: function( type, clearQueue, gotoEnd ) {\n var stopQueue = function( hooks ) {\n var stop = hooks.stop;\n delete hooks.stop;\n stop( gotoEnd );\n };\n\n if ( typeof type !== \"string\" ) {\n gotoEnd = clearQueue;\n clearQueue = type;\n type = undefined;\n }\n if ( clearQueue && type !== false ) {\n this.queue( type || \"fx\", [] );\n }\n\n return this.each(function() {\n var dequeue = true,\n index = type != null && type + \"queueHooks\",\n timers = jQuery.timers,\n data = jQuery._data( this );\n\n if ( index ) {\n if ( data[ index ] && data[ index ].stop ) {\n stopQueue( data[ index ] );\n }\n } else {\n for ( index in data ) {\n if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {\n stopQueue( data[ index ] );\n }\n }\n }\n\n for ( index = timers.length; index--; ) {\n if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {\n timers[ index ].anim.stop( gotoEnd );\n dequeue = false;\n timers.splice( index, 1 );\n }\n }\n\n // start the next in the queue if the last step wasn't forced\n // timers currently will call their complete callbacks, which will dequeue\n // but only if they were gotoEnd\n if ( dequeue || !gotoEnd ) {\n jQuery.dequeue( this, type );\n }\n });\n }\n});\n\n// Generate parameters to create a standard animation\nfunction genFx( type, includeWidth ) {\n var which,\n attrs = { height: type },\n i = 0;\n\n // if we include width, step value is 1 to do all cssExpand values,\n // if we don't include width, step value is 2 to skip over Left and Right\n includeWidth = includeWidth? 1 : 0;\n for( ; i < 4 ; i += 2 - includeWidth ) {\n which = cssExpand[ i ];\n attrs[ \"margin\" + which ] = attrs[ \"padding\" + which ] = type;\n }\n\n if ( includeWidth ) {\n attrs.opacity = attrs.width = type;\n }\n\n return attrs;\n}\n\n// Generate shortcuts for custom animations\njQuery.each({\n slideDown: genFx(\"show\"),\n slideUp: genFx(\"hide\"),\n slideToggle: genFx(\"toggle\"),\n fadeIn: { opacity: \"show\" },\n fadeOut: { opacity: \"hide\" },\n fadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n jQuery.fn[ name ] = function( speed, easing, callback ) {\n return this.animate( props, speed, easing, callback );\n };\n});\n\njQuery.speed = function( speed, easing, fn ) {\n var opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n complete: fn || !fn && easing ||\n jQuery.isFunction( speed ) && speed,\n duration: speed,\n easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing\n };\n\n opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === \"number\" ? opt.duration :\n opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;\n\n // normalize opt.queue - true/undefined/null -> \"fx\"\n if ( opt.queue == null || opt.queue === true ) {\n opt.queue = \"fx\";\n }\n\n // Queueing\n opt.old = opt.complete;\n\n opt.complete = function() {\n if ( jQuery.isFunction( opt.old ) ) {\n opt.old.call( this );\n }\n\n if ( opt.queue ) {\n jQuery.dequeue( this, opt.queue );\n }\n };\n\n return opt;\n};\n\njQuery.easing = {\n linear: function( p ) {\n return p;\n },\n swing: function( p ) {\n return 0.5 - Math.cos( p*Math.PI ) / 2;\n }\n};\n\njQuery.timers = [];\njQuery.fx = Tween.prototype.init;\njQuery.fx.tick = function() {\n var timer,\n timers = jQuery.timers,\n i = 0;\n\n fxNow = jQuery.now();\n\n for ( ; i < timers.length; i++ ) {\n timer = timers[ i ];\n // Checks the timer has not already been removed\n if ( !timer() && timers[ i ] === timer ) {\n timers.splice( i--, 1 );\n }\n }\n\n if ( !timers.length ) {\n jQuery.fx.stop();\n }\n fxNow = undefined;\n};\n\njQuery.fx.timer = function( timer ) {\n if ( timer() && jQuery.timers.push( timer ) && !timerId ) {\n timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );\n }\n};\n\njQuery.fx.interval = 13;\n\njQuery.fx.stop = function() {\n clearInterval( timerId );\n timerId = null;\n};\n\njQuery.fx.speeds = {\n slow: 600,\n fast: 200,\n // Default speed\n _default: 400\n};\n\n// Back Compat <1.8 extension point\njQuery.fx.step = {};\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n jQuery.expr.filters.animated = function( elem ) {\n return jQuery.grep(jQuery.timers, function( fn ) {\n return elem === fn.elem;\n }).length;\n };\n}\nvar rroot = /^(?:body|html)$/i;\n\njQuery.fn.offset = function( options ) {\n if ( arguments.length ) {\n return options === undefined ?\n this :\n this.each(function( i ) {\n jQuery.offset.setOffset( this, options, i );\n });\n }\n\n var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft,\n box = { top: 0, left: 0 },\n elem = this[ 0 ],\n doc = elem && elem.ownerDocument;\n\n if ( !doc ) {\n return;\n }\n\n if ( (body = doc.body) === elem ) {\n return jQuery.offset.bodyOffset( elem );\n }\n\n docElem = doc.documentElement;\n\n // Make sure it's not a disconnected DOM node\n if ( !jQuery.contains( docElem, elem ) ) {\n return box;\n }\n\n // If we don't have gBCR, just use 0,0 rather than error\n // BlackBerry 5, iOS 3 (original iPhone)\n if ( typeof elem.getBoundingClientRect !== \"undefined\" ) {\n box = elem.getBoundingClientRect();\n }\n win = getWindow( doc );\n clientTop = docElem.clientTop || body.clientTop || 0;\n clientLeft = docElem.clientLeft || body.clientLeft || 0;\n scrollTop = win.pageYOffset || docElem.scrollTop;\n scrollLeft = win.pageXOffset || docElem.scrollLeft;\n return {\n top: box.top + scrollTop - clientTop,\n left: box.left + scrollLeft - clientLeft\n };\n};\n\njQuery.offset = {\n\n bodyOffset: function( body ) {\n var top = body.offsetTop,\n left = body.offsetLeft;\n\n if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {\n top += parseFloat( jQuery.css(body, \"marginTop\") ) || 0;\n left += parseFloat( jQuery.css(body, \"marginLeft\") ) || 0;\n }\n\n return { top: top, left: left };\n },\n\n setOffset: function( elem, options, i ) {\n var position = jQuery.css( elem, \"position\" );\n\n // set position first, in-case top/left are set even on static elem\n if ( position === \"static\" ) {\n elem.style.position = \"relative\";\n }\n\n var curElem = jQuery( elem ),\n curOffset = curElem.offset(),\n curCSSTop = jQuery.css( elem, \"top\" ),\n curCSSLeft = jQuery.css( elem, \"left\" ),\n calculatePosition = ( position === \"absolute\" || position === \"fixed\" ) && jQuery.inArray(\"auto\", [curCSSTop, curCSSLeft]) > -1,\n props = {}, curPosition = {}, curTop, curLeft;\n\n // need to be able to calculate position if either top or left is auto and position is either absolute or fixed\n if ( calculatePosition ) {\n curPosition = curElem.position();\n curTop = curPosition.top;\n curLeft = curPosition.left;\n } else {\n curTop = parseFloat( curCSSTop ) || 0;\n curLeft = parseFloat( curCSSLeft ) || 0;\n }\n\n if ( jQuery.isFunction( options ) ) {\n options = options.call( elem, i, curOffset );\n }\n\n if ( options.top != null ) {\n props.top = ( options.top - curOffset.top ) + curTop;\n }\n if ( options.left != null ) {\n props.left = ( options.left - curOffset.left ) + curLeft;\n }\n\n if ( \"using\" in options ) {\n options.using.call( elem, props );\n } else {\n curElem.css( props );\n }\n }\n};\n\n\njQuery.fn.extend({\n\n position: function() {\n if ( !this[0] ) {\n return;\n }\n\n var elem = this[0],\n\n // Get *real* offsetParent\n offsetParent = this.offsetParent(),\n\n // Get correct offsets\n offset = this.offset(),\n parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();\n\n // Subtract element margins\n // note: when an element has margin: auto the offsetLeft and marginLeft\n // are the same in Safari causing offset.left to incorrectly be 0\n offset.top -= parseFloat( jQuery.css(elem, \"marginTop\") ) || 0;\n offset.left -= parseFloat( jQuery.css(elem, \"marginLeft\") ) || 0;\n\n // Add offsetParent borders\n parentOffset.top += parseFloat( jQuery.css(offsetParent[0], \"borderTopWidth\") ) || 0;\n parentOffset.left += parseFloat( jQuery.css(offsetParent[0], \"borderLeftWidth\") ) || 0;\n\n // Subtract the two offsets\n return {\n top: offset.top - parentOffset.top,\n left: offset.left - parentOffset.left\n };\n },\n\n offsetParent: function() {\n return this.map(function() {\n var offsetParent = this.offsetParent || document.body;\n while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, \"position\") === \"static\") ) {\n offsetParent = offsetParent.offsetParent;\n }\n return offsetParent || document.body;\n });\n }\n});\n\n\n// Create scrollLeft and scrollTop methods\njQuery.each( {scrollLeft: \"pageXOffset\", scrollTop: \"pageYOffset\"}, function( method, prop ) {\n var top = /Y/.test( prop );\n\n jQuery.fn[ method ] = function( val ) {\n return jQuery.access( this, function( elem, method, val ) {\n var win = getWindow( elem );\n\n if ( val === undefined ) {\n return win ? (prop in win) ? win[ prop ] :\n win.document.documentElement[ method ] :\n elem[ method ];\n }\n\n if ( win ) {\n win.scrollTo(\n !top ? val : jQuery( win ).scrollLeft(),\n top ? val : jQuery( win ).scrollTop()\n );\n\n } else {\n elem[ method ] = val;\n }\n }, method, val, arguments.length, null );\n };\n});\n\nfunction getWindow( elem ) {\n return jQuery.isWindow( elem ) ?\n elem :\n elem.nodeType === 9 ?\n elem.defaultView || elem.parentWindow :\n false;\n}\n// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods\njQuery.each( { Height: \"height\", Width: \"width\" }, function( name, type ) {\n jQuery.each( { padding: \"inner\" + name, content: type, \"\": \"outer\" + name }, function( defaultExtra, funcName ) {\n // margin is only for outerHeight, outerWidth\n jQuery.fn[ funcName ] = function( margin, value ) {\n var chainable = arguments.length && ( defaultExtra || typeof margin !== \"boolean\" ),\n extra = defaultExtra || ( margin === true || value === true ? \"margin\" : \"border\" );\n\n return jQuery.access( this, function( elem, type, value ) {\n var doc;\n\n if ( jQuery.isWindow( elem ) ) {\n // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there\n // isn't a whole lot we can do. See pull request at this URL for discussion:\n // https://github.com/jquery/jquery/pull/764\n return elem.document.documentElement[ \"client\" + name ];\n }\n\n // Get document width or height\n if ( elem.nodeType === 9 ) {\n doc = elem.documentElement;\n\n // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest\n // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.\n return Math.max(\n elem.body[ \"scroll\" + name ], doc[ \"scroll\" + name ],\n elem.body[ \"offset\" + name ], doc[ \"offset\" + name ],\n doc[ \"client\" + name ]\n );\n }\n\n return value === undefined ?\n // Get width or height on the element, requesting but not forcing parseFloat\n jQuery.css( elem, type, value, extra ) :\n\n // Set width or height on the element\n jQuery.style( elem, type, value, extra );\n }, type, chainable ? margin : undefined, chainable, null );\n };\n });\n});\n// Expose jQuery to the global object\nwindow.jQuery = window.$ = jQuery;\n\n// Expose jQuery as an AMD module, but only for AMD loaders that\n// understand the issues with loading multiple versions of jQuery\n// in a page that all might call define(). The loader will indicate\n// they have special allowances for multiple jQuery versions by\n// specifying define.amd.jQuery = true. Register as a named module,\n// since jQuery can be concatenated with other files that may use define,\n// but not use a proper concatenation script that understands anonymous\n// AMD modules. A named AMD is safest and most robust way to register.\n// Lowercase jquery is used because AMD module names are derived from\n// file names, and jQuery is normally delivered in a lowercase file name.\n// Do this after creating the global so that if an AMD module wants to call\n// noConflict to hide this version of jQuery, it will work.\nif ( typeof define === \"function\" && define.amd && define.amd.jQuery ) {\n define( \"jquery\", [], function () { return jQuery; } );\n}\n\n})( window );",
+ "settings":
+ {
+ "buffer_size": 287804,
+ "line_ending": "Unix",
+ "name": "/*!"
+ }
+ },
+ {
+ "file": "test/integration/test-submit.js",
+ "settings":
+ {
+ "buffer_size": 3638,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "file": "test/integration/test-submit-custom.js",
+ "settings":
+ {
+ "buffer_size": 3684,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "file": "test/integration/test-http-response.js",
+ "settings":
+ {
+ "buffer_size": 3109,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "file": "Readme.md",
+ "settings":
+ {
+ "buffer_size": 2774,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "contents": "var CRLF = '\\r\\n';\nvar form = new FormData();\n\nvar options = {\n header: CRLF + '--' + form.getBoundary() + CRLF + 'X-Custom-Header: 123' + CRLF + CRLF,\n knownLength: 1\n};\n\nform.append('my_buffer', buffer, options);\n\nform.submit('http://example.com/', function(err, res) {\n if (err) throw err;\n console.log('Done');\n});",
+ "settings":
+ {
+ "buffer_size": 322,
+ "line_ending": "Unix"
+ }
+ },
+ {
+ "file": "test/run.js",
+ "settings":
+ {
+ "buffer_size": 121,
+ "line_ending": "Unix"
+ }
+ }
+ ],
+ "build_system": "",
+ "command_palette":
+ {
+ "height": 87.0,
+ "selected_items":
+ [
+ [
+ "Package Control: in",
+ "Package Control: Install Package"
+ ],
+ [
+ "Package Control: ins",
+ "Package Control: Install Package"
+ ],
+ [
+ "ins",
+ "Package Control: Install Package"
+ ],
+ [
+ "insta",
+ "Package Control: Install Package"
+ ]
+ ],
+ "width": 467.0
+ },
+ "console":
+ {
+ "height": 125.0
+ },
+ "distraction_free":
+ {
+ "menu_visible": true,
+ "show_minimap": false,
+ "show_open_files": false,
+ "show_tabs": false,
+ "side_bar_visible": false,
+ "status_bar_visible": false
+ },
+ "file_history":
+ [
+ "/Users/alexi/Dropbox/Projects/node-form-data/test/integration/test-http-respone.js",
+ "/Users/alexi/Dropbox/Projects/node-form-data/test/run.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/sftp-config-alt.json",
+ "/Users/alexi/Desktop/test/file.txt",
+ "/Users/alexi/Desktop/stuff/kodak/web.js",
+ "/Users/alexi/Desktop/test/test1.js",
+ "/Users/alexi/Desktop/test/spdy.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/bin/echo.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/static/test.html",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/bin/passthrough_stream.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/request/main.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/request/tests/test-pipes.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/static/a/main.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/flickr/lib/flickr.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/socket.io/lib/socket.io.js",
+ "/var/folders/xn/475pdrpd72n4s6gdh4y_c2dm0000gn/T/sublime-sftp-browse-1342313240/mapped/var/www/libereco.ia.gs/node_modules/oauth/lib/oauth.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/flickr/node_modules/oauth/lib/oauth.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/flickr/index.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/node_modules/socket.io/index.js",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/sftp-config.json",
+ "/Users/alexi/Dropbox/Projects/libereco.ia.gs/public/index.html",
+ "/Users/alexi/Desktop/kodak/dns.js",
+ "/Users/alexi/Dropbox/Projects/500.ia.gs/htdocs/s/fonts.css",
+ "/Users/alexi/Dropbox/Projects/ia.gs/sftp-config.json",
+ "/Users/alexi/Downloads/fabric_plaid.png",
+ "/Users/alexi/Library/Application Support/Sublime Text 2/Packages/Default/Global.sublime-settings",
+ "/Users/alexi/Dropbox/Projects/ia.gs/node_modules/director/lib/director/router.js",
+ "/Users/alexi/Dropbox/Projects/ia.gs/web/e/404.html",
+ "/Users/alexi/Dropbox/Projects/ia.gs/web/e/500.html",
+ "/Users/alexi/Dropbox/Projects/ia.gs/node_modules/director/lib/director/http/index.js",
+ "/Users/alexi/Library/Application Support/Sublime Text 2/Packages/SFTP/SFTP.sublime-settings",
+ "/Users/alexi/Library/Application Support/Sublime Text 2/Packages/SFTP/Default (OSX).sublime-keymap",
+ "/Users/alexi/Sites/new/sftp-config.json",
+ "/Users/alexi/Sites/new/polarbear.css",
+ "/Users/alexi/Library/Application Support/Sublime Text 2/Packages/User/Base File.sublime-settings",
+ "/Users/alexi/Library/Application Support/Sublime Text 2/Packages/Default/Base File.sublime-settings",
+ "/Users/alexi/Sites/new/include/Controllers/Home/HomepageController.php"
+ ],
+ "find":
+ {
+ "height": 35.0
+ },
+ "find_in_files":
+ {
+ "height": 0.0,
+ "where_history":
+ [
+ ""
+ ]
+ },
+ "find_state":
+ {
+ "case_sensitive": false,
+ "find_history":
+ [
+ "readable",
+ "argument",
+ "mikeal",
+ "return;",
+ "throw arguments[1]",
+ "arguments[1]",
+ "throw arguments",
+ "throw arguments[1]",
+ "path",
+ "conso",
+ "isStreamLike",
+ "parseUrl",
+ "stream",
+ "node.js",
+ "stream",
+ "http://libereco.ia.gs/",
+ "flickr_message_nopro",
+ "auth:done",
+ "con",
+ "console",
+ "photos",
+ "isReadStream",
+ "Buffer",
+ "Bufer",
+ "multipart",
+ "sig",
+ "api",
+ "api_",
+ "api_sig",
+ "writeFile",
+ "googledoodle.png",
+ "attachment",
+ "_write",
+ "fs",
+ "mime",
+ "_putOrPost",
+ "_performSecureRequest",
+ "8034",
+ "try",
+ "paramsToQueryString",
+ "_executeOAuthAPIRequest",
+ "oauth_client",
+ "_createClient",
+ "authorization",
+ "body",
+ "_performSecureRequest",
+ "query",
+ "io.",
+ ".listen",
+ "io",
+ "config",
+ "set",
+ "console",
+ "'next'",
+ "console",
+ "_asyncEverySeries",
+ "runlist",
+ "function",
+ "async",
+ "local",
+ "Find schools",
+ "What's up"
+ ],
+ "highlight": true,
+ "in_selection": false,
+ "preserve_case": false,
+ "regex": false,
+ "replace_history":
+ [
+ ],
+ "reverse": false,
+ "show_context": true,
+ "use_buffer2": true,
+ "whole_word": false,
+ "wrap": true
+ },
+ "groups":
+ [
+ {
+ "selected": 1,
+ "sheets":
+ [
+ {
+ "buffer": 0,
+ "file": "test/integration/test-pipe.js",
+ "settings":
+ {
+ "buffer_size": 3625,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 654,
+ 673
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 18,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 238.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 1,
+ "file": "package.json",
+ "settings":
+ {
+ "buffer_size": 673,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 282,
+ 282
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 4,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JSON.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 0.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 2,
+ "file": "lib/form_data.js",
+ "settings":
+ {
+ "buffer_size": 7510,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 1940,
+ 1940
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 65,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 658.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 3,
+ "settings":
+ {
+ "buffer_size": 287804,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 8273,
+ 8279
+ ]
+ ],
+ "settings":
+ {
+ "auto_name": "/*!",
+ "function_name_status_row": 290,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 5683.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 4,
+ "file": "test/integration/test-submit.js",
+ "settings":
+ {
+ "buffer_size": 3638,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 3556,
+ 3556
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 111,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 1200.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 5,
+ "file": "test/integration/test-submit-custom.js",
+ "settings":
+ {
+ "buffer_size": 3684,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 3449,
+ 3449
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 104,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 1268.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 6,
+ "file": "test/integration/test-http-response.js",
+ "settings":
+ {
+ "buffer_size": 3109,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 2156,
+ 2156
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 88,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 59.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 7,
+ "file": "Readme.md",
+ "settings":
+ {
+ "buffer_size": 2774,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 0,
+ 0
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 0,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/Markdown/Markdown.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 996.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 8,
+ "settings":
+ {
+ "buffer_size": 322,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 0,
+ 322
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 0,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 0.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ },
+ {
+ "buffer": 9,
+ "file": "test/run.js",
+ "settings":
+ {
+ "buffer_size": 121,
+ "regions":
+ {
+ },
+ "selection":
+ [
+ [
+ 76,
+ 76
+ ]
+ ],
+ "settings":
+ {
+ "function_name_status_row": 4,
+ "incomplete_sync": null,
+ "remote_loading": false,
+ "synced": false,
+ "syntax": "Packages/JavaScript/JavaScript.tmLanguage"
+ },
+ "translation.x": 0.0,
+ "translation.y": 0.0,
+ "zoom_level": 1.0
+ },
+ "type": "text"
+ }
+ ]
+ }
+ ],
+ "incremental_find":
+ {
+ "height": 0.0
+ },
+ "input":
+ {
+ "height": 31.0
+ },
+ "layout":
+ {
+ "cells":
+ [
+ [
+ 0,
+ 0,
+ 1,
+ 1
+ ]
+ ],
+ "cols":
+ [
+ 0.0,
+ 1.0
+ ],
+ "rows":
+ [
+ 0.0,
+ 1.0
+ ]
+ },
+ "menu_visible": true,
+ "output.sftp":
+ {
+ "height": 108.0
+ },
+ "replace":
+ {
+ "height": 0.0
+ },
+ "save_all_on_build": true,
+ "select_file":
+ {
+ "height": 0.0,
+ "selected_items":
+ [
+ [
+ "homepagecon",
+ "include/Controllers/Home/HomepageController.php"
+ ],
+ [
+ "polar",
+ "polarbear.css"
+ ],
+ [
+ "homepageco",
+ "include/Controllers/Home/HomepageController.php"
+ ],
+ [
+ "homepage.js",
+ "include/js/application/homepage/homepage.js"
+ ]
+ ],
+ "width": 0.0
+ },
+ "select_project":
+ {
+ "height": 0.0,
+ "selected_items":
+ [
+ ],
+ "width": 0.0
+ },
+ "show_minimap": true,
+ "show_open_files": true,
+ "show_tabs": true,
+ "side_bar_visible": true,
+ "side_bar_width": 250.0,
+ "status_bar_visible": true
+}
+++ /dev/null
-[submodule "deps/nodeunit"]
- path = deps/nodeunit
- url = git://github.com/caolan/nodeunit.git
-[submodule "deps/UglifyJS"]
- path = deps/UglifyJS
- url = https://github.com/mishoo/UglifyJS.git
-[submodule "deps/nodelint"]
- path = deps/nodelint
- url = https://github.com/tav/nodelint.git
+++ /dev/null
-deps
-dist
-test
-nodelint.cfg
\ No newline at end of file
+++ /dev/null
-PACKAGE = asyncjs
-NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
-CWD := $(shell pwd)
-NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit
-UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs
-NODELINT = $(CWD)/node_modules/nodelint/nodelint
-
-BUILDDIR = dist
-
-all: clean test build
-
-build: $(wildcard lib/*.js)
- mkdir -p $(BUILDDIR)
- $(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js
-
-test:
- $(NODEUNIT) test
-
-clean:
- rm -rf $(BUILDDIR)
-
-lint:
- $(NODELINT) --config nodelint.cfg lib/async.js
-
-.PHONY: test build all
browser.
Async provides around 20 functions that include the usual 'functional'
-suspects (map, reduce, filter, forEach…) as well as some common patterns
+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
- async.map(['file1','file2','file3'], fs.stat, function(err, results){
- // results is now an array of stats for each file
- });
+```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'], path.exists, function(results){
- // results now equals an array of the existing files
- });
+async.filter(['file1','file2','file3'], path.exists, function(results){
+ // results now equals an array of the existing files
+});
- async.parallel([
- function(){ ... },
- function(){ ... }
- ], callback);
+async.parallel([
+ function(){ ... },
+ function(){ ... }
+], callback);
- async.series([
- function(){ ... },
- function(){ ... }
- ]);
+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
-Releases are available for download from
-[GitHub](http://github.com/caolan/async/downloads).
+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) - 17.5kb Uncompressed
-
-__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped
-
+__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed
## In the Browser
-So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
+So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
- <script type="text/javascript" src="async.js"></script>
- <script type="text/javascript">
+```html
+<script type="text/javascript" src="async.js"></script>
+<script type="text/javascript">
- async.map(data, asyncProcess, function(err, results){
- alert(results);
- });
-
- </script>
+ async.map(data, asyncProcess, function(err, results){
+ alert(results);
+ });
+</script>
+```
## Documentation
### Collections
-* [forEach](#forEach)
+* [each](#each)
* [map](#map)
* [filter](#filter)
* [reject](#reject)
* [series](#series)
* [parallel](#parallel)
* [whilst](#whilst)
+* [doWhilst](#doWhilst)
* [until](#until)
+* [doUntil](#doUntil)
+* [forever](#forever)
* [waterfall](#waterfall)
+* [compose](#compose)
+* [applyEach](#applyEach)
* [queue](#queue)
+* [cargo](#cargo)
* [auto](#auto)
* [iterator](#iterator)
* [apply](#apply)
* [nextTick](#nextTick)
+* [times](#times)
+* [timesSeries](#timesSeries)
### Utils
## Collections
<a name="forEach" />
-### forEach(arr, iterator, callback)
+<a name="each" />
+### each(arr, iterator, callback)
Applies an iterator function to each item in an array, 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 this callback, the main
-callback for the forEach function is immediately called with the error.
+callback for the each 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.
* arr - An array to iterate over.
* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed.
+ The iterator is passed a callback(err) which must be called once it has
+ completed. If no error has occured, the callback should be run without
+ arguments or with an explicit null argument.
* callback(err) - A callback which is called after all the iterator functions
have finished, or an error has occurred.
__Example__
- // assuming openFiles is an array of file names and saveFile is a function
- // to save the modified contents of that file:
+```js
+// assuming openFiles is an array of file names and saveFile is a function
+// to save the modified contents of that file:
- async.forEach(openFiles, saveFile, function(err){
- // if any of the saves produced an error, err would equal that error
- });
+async.each(openFiles, saveFile, function(err){
+ // if any of the saves produced an error, err would equal that error
+});
+```
---------------------------------------
<a name="forEachSeries" />
-### forEachSeries(arr, iterator, callback)
+<a name="eachSeries" />
+### eachSeries(arr, iterator, callback)
-The same as forEach only the iterator is applied to each item in the array in
+The same as each only the iterator is applied to each item in the array in
series. The next iterator is only called once the current one has completed
processing. This means the iterator functions will complete in order.
---------------------------------------
<a name="forEachLimit" />
-### forEachLimit(arr, limit, iterator, callback)
+<a name="eachLimit" />
+### eachLimit(arr, limit, iterator, callback)
+
+The same as each only no more than "limit" iterators will be simultaneously
+running at any time.
-The same as forEach only the iterator is applied to batches of items in the
-array, in series. The next batch of iterators is only called once the current
-one has completed processing.
+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 - How many items should be in each batch.
+* limit - The maximum number of iterators to run at any time.
* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed.
+ The iterator is passed a callback(err) which must be called once it has
+ completed. If no error has occured, the callback should be run without
+ arguments or with an explicit null argument.
* callback(err) - A callback which is called after all the iterator functions
have finished, or an error has occurred.
__Example__
- // Assume documents is an array of JSON objects and requestApi is a
- // function that interacts with a rate-limited REST api.
+```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
+});
+```
- async.forEachLimit(documents, 20, requestApi, function(err){
- // if any of the saves produced an error, err would equal that error
- });
---------------------------------------
<a name="map" />
* arr - An array to iterate over.
* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed
- with an error (which can be null) and a transformed item.
+ 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 after all the iterator
functions have finished, or an error has occurred. Results is an array of the
transformed items from the original array.
__Example__
- async.map(['file1','file2','file3'], fs.stat, function(err, results){
- // results is now an array of stats for each file
- });
+```js
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
---------------------------------------
---------------------------------------
+<a name="mapLimit" />
+### mapLimit(arr, limit, iterator, callback)
+
+The same as map only no more than "limit" iterators 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 iterators to run at any time.
+* iterator(item, callback) - A function to apply to each item in the array.
+ 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 after all the iterator
+ functions have finished, or an error has occurred. Results is an array of the
+ transformed items from the original array.
+
+__Example__
+
+```js
+async.map(['file1','file2','file3'], 1, fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
<a name="filter" />
### filter(arr, iterator, callback)
* arr - An array to iterate over.
* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed.
+ 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__
- async.filter(['file1','file2','file3'], path.exists, function(results){
- // results now equals an array of the existing files
- });
+```js
+async.filter(['file1','file2','file3'], path.exists, function(results){
+ // results now equals an array of the existing files
+});
+```
---------------------------------------
<a name="rejectSeries" />
### rejectSeries(arr, iterator, callback)
-The same as filter, only the iterator is applied to each item in the array
+The same as reject, only the iterator is applied to each item in the array
in series.
split a call to this function into a parallel map, 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 its probably a good idea to do so.
+reducing it then it's probably a good idea to do so.
__Arguments__
* 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 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, 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__
- 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
+```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
+});
+```
---------------------------------------
* arr - An array to iterate over.
* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed.
+ 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
__Example__
- async.detect(['file1','file2','file3'], path.exists, function(result){
- // result now equals the first file in the list that exists
- });
+```js
+async.detect(['file1','file2','file3'], path.exists, function(result){
+ // result now equals the first file in the list that exists
+});
+```
---------------------------------------
* arr - An array to iterate over.
* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed
- with an error (which can be null) and a value to use as the sort criteria.
+ 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 has occurred. Results is the items from
the original array sorted by the values returned by the iterator calls.
__Example__
- 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
+```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
+});
+```
---------------------------------------
* arr - An array to iterate over.
* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed.
+ 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__
- async.some(['file1','file2','file3'], path.exists, function(result){
- // if result is true then at least one of the files exists
- });
+```js
+async.some(['file1','file2','file3'], path.exists, function(result){
+ // if result is true then at least one of the files exists
+});
+```
---------------------------------------
* arr - An array to iterate over.
* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed.
+ 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__
- async.every(['file1','file2','file3'], path.exists, function(result){
- // if result is true then every file exists
- });
+```js
+async.every(['file1','file2','file3'], path.exists, function(result){
+ // if result is true then every file exists
+});
+```
---------------------------------------
* arr - An array to iterate over
* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback which must be called once it has completed
- with an error (which can be null) and an array of results.
+ 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 has occurred. Results is an array containing
the concatenated results of the iterator function.
__Example__
- async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
- // files is now a list of filenames that exist in the 3 directories
- });
+```js
+async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
+ // files is now a list of filenames that exist in the 3 directories
+});
+```
---------------------------------------
__Arguments__
* tasks - An array or object containing functions to run, each function is passed
- a callback it must call on completion.
+ a callback(err, result) it must call on completion with an error (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 an array of all the arguments passed to
- the callbacks used in the array.
+ have completed. This function gets a results array (or object) containing all
+ the result arguments passed to the task callbacks.
__Example__
- 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']
- });
+```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);
- },
+// an example using an object instead of an array
+async.series({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
},
- function(err, results) {
- // results is now equal to: {one: 1, two: 2}
- });
-
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ }
+},
+function(err, results) {
+ // results is now equal to: {one: 1, two: 2}
+});
+```
---------------------------------------
__Arguments__
-* tasks - An array or object containing functions to run, each function is passed a
- callback it must call on completion.
+* 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 (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 an array of all the arguments passed to
- the callbacks used in the array.
+ have completed. This function gets a results array (or object) containing all
+ the result arguments passed to the task callbacks.
__Example__
- 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.
- });
+```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);
- },
+// an example using an object instead of an array
+async.parallel({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
},
- function(err, results) {
- // results is now equals to: {one: 1, two: 2}
- });
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ }
+},
+function(err, results) {
+ // results is now equals to: {one: 1, two: 2}
+});
+```
+
+---------------------------------------
+
+<a name="parallel" />
+### parallelLimit(tasks, limit, [callback])
+The same as parallel only the 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 (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.
---------------------------------------
* test() - synchronous truth test to perform before each execution of fn.
* fn(callback) - A function to call each time the test passes. The function is
- passed a callback which must be called once it has completed with an optional
- error as the first argument.
+ passed a callback(err) which must be called once it has completed with an
+ optional error argument.
* callback(err) - A callback which is called after the test fails and repeated
execution of fn has stopped.
__Example__
- var count = 0;
+```js
+var count = 0;
- async.whilst(
- function () { return count < 5; },
- function (callback) {
- count++;
- setTimeout(callback, 1000);
- },
- function (err) {
- // 5 seconds have passed
- }
- );
+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. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
---------------------------------------
The inverse of async.whilst.
+---------------------------------------
+
+<a name="doUntil" />
+### doUntil(fn, test, callback)
+
+Like doWhilst except the test is inverted. Note the argument ordering differs from `until`.
+
+---------------------------------------
+
+<a name="forever" />
+### forever(fn, callback)
+
+Calls the asynchronous function 'fn' repeatedly, in series, indefinitely.
+If an error is passed to fn's callback then 'callback' is called with the
+error, otherwise it will never be called.
---------------------------------------
__Arguments__
-* tasks - An array of functions to run, each function is passed a callback it
- must call on completion.
+* 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__
- async.waterfall([
- function(callback){
- callback(null, 'one', 'two');
- },
- function(arg1, arg2, callback){
- callback(null, 'three');
- },
- function(arg1, callback){
- // arg1 now equals 'three'
- callback(null, 'done');
- }
- ], function (err, result) {
- // result now equals 'done'
- });
+```js
+async.waterfall([
+ function(callback){
+ callback(null, 'one', 'two');
+ },
+ function(arg1, arg2, callback){
+ 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="applyEach" />
+### applyEach(fns, args..., callback)
+
+Applies the provided arguments to each function in the array, calling the
+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 only the functions are applied in series.
---------------------------------------
__Arguments__
* worker(task, callback) - An asynchronous function for processing a queued
- task.
+ 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.
* push(task, [callback]) - add a new 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.
+* 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 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__
- // create a queue object with concurrency 2
+```js
+// create a queue object with concurrency 2
- var q = async.queue(function (task, callback) {
- console.log('hello ' + task.name);
- callback();
- }, 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');
- }
+// assign a callback
+q.drain = function() {
+ console.log('all items have been processed');
+}
- // add some items to the queue
+// 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');
- });
+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)
+// add some items to the queue (batch-wise)
+
+q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
+ console.log('finished processing bar');
+});
+
+// add some items to the front of the queue
+
+q.unshift({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+```
+
+---------------------------------------
+
+<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 is available. Once
+the worker has completed some tasks, each callback of those tasks is called.
+
+__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 error as an 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]) - add a new 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);
- q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
- console.log('finished processing bar');
- });
+// 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');
+});
+```
---------------------------------------
will be called immediately 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 literal containing named functions or an array of
requirements, with the function itself the last item in the array. The key
- used for each function or array is used when specifying requirements. The
- syntax is easier to understand by looking at the example.
+ used for each function or array is used when specifying requirements. 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. The callback will receive an error as an argument
- if any tasks pass an error to their callback. If all tasks complete
- successfully, it will receive an object containing their results.
+ if any tasks pass an error to their callback. Results will always be passed
+ but if an error occurred, no other tasks will be performed, and the results
+ object will only contain partial results.
+
__Example__
- async.auto({
- get_data: function(callback){
- // async code to get some data
- },
- make_folder: function(callback){
- // async code to create a directory to store a file in
- // this is run at the same time as getting the data
- },
- write_file: ['get_data', 'make_folder', function(callback){
- // 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){
- // once the file is written let's email a link to it...
- // results.write_file contains the filename returned by write_file.
- }]
- });
+```js
+async.auto({
+ get_data: function(callback){
+ // async code to get some data
+ },
+ make_folder: function(callback){
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ },
+ write_file: ['get_data', 'make_folder', function(callback){
+ // 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){
+ // once the file is written let's email a link to it...
+ // results.write_file contains the filename returned by write_file.
+ }]
+});
+```
This is a fairly trivial example, but to do this using the basic parallel and
series functions would look like this:
- async.parallel([
+```js
+async.parallel([
+ function(callback){
+ // async code to get some data
+ },
+ function(callback){
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ }
+],
+function(err, results){
+ async.series([
function(callback){
- // async code to get some data
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
},
function(callback){
- // async code to create a directory to store a file in
- // this is run at the same time as getting the data
+ // once the file is written let's email a link to it...
}
- ],
- function(results){
- async.series([
- function(callback){
- // once there is some data and the directory exists,
- // write the data to a file in the directory
- },
- email_link: function(callback){
- // once the file is written let's email a link to it...
- }
- ]);
- });
+ ]);
+});
+```
For a complicated series of async tasks using the auto function makes adding
new tasks much easier and makes the code more readable.
### iterator(tasks)
Creates an iterator function which calls the next function in the array,
-returning a continuation to call the next one after that. Its also possible to
+returning a continuation to call the next one after that. It's also possible to
'peek' the next iterator by doing iterator.next().
This function is used internally by the async module but can be useful when
__Arguments__
-* tasks - An array of functions to run, each function is passed a callback it
- must call on completion.
+* tasks - An array of functions to run.
__Example__
- 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'
-
+```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'
+```
---------------------------------------
__Example__
- // using apply
+```js
+// using apply
- async.parallel([
- async.apply(fs.writeFile, 'testfile1', 'test1'),
- async.apply(fs.writeFile, 'testfile2', 'test2'),
- ]);
+async.parallel([
+ async.apply(fs.writeFile, 'testfile1', 'test1'),
+ async.apply(fs.writeFile, 'testfile2', 'test2'),
+]);
- // the same process without using apply
+// the same process without using apply
- async.parallel([
- function(callback){
- fs.writeFile('testfile1', 'test1', callback);
- },
- function(callback){
- fs.writeFile('testfile2', 'test2', callback);
- },
- ]);
+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:
- node> var fn = async.apply(sys.puts, 'one');
- node> fn('two', 'three');
- one
- two
- three
+```js
+node> var fn = async.apply(sys.puts, 'one');
+node> fn('two', 'three');
+one
+two
+three
+```
---------------------------------------
### nextTick(callback)
Calls the callback on a later loop around the event loop. In node.js this just
-calls process.nextTick, in the browser it falls back to setTimeout(callback, 0),
-which means other higher priority events may precede the execution of the callback.
+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 the callback.
This is used internally for browser-compatibility purposes.
__Example__
- var call_order = [];
- async.nextTick(function(){
- call_order.push('two');
- // call_order now equals ['one','two]
- });
- call_order.push('one')
+```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 n times and accumulates results in the same manner
+you would use with async.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 only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. The results array will be in the same order as the original.
## Utils
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 you to proxy and cache results from.
__Example__
- var slow_fn = function (name, callback) {
- // do something
- callback(null, result);
- };
- var fn = async.memoize(slow_fn);
+```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
- });
+// fn can now be used as if it were slow_fn
+fn('some name', function () {
+ // callback
+});
+```
<a name="unmemoize" />
### unmemoize(fn)
__Example__
- var hello = function(name, callback){
- setTimeout(function(){
- callback(null, 'hello ' + name);
- }, 1000);
- };
-
- node> async.log(hello, 'world');
- 'hello world'
-
+```js
+var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, 'hello ' + name);
+ }, 1000);
+};
+```
+```js
+node> async.log(hello, 'world');
+'hello world'
+```
---------------------------------------
__Example__
- var hello = function(name, callback){
- setTimeout(function(){
- callback(null, {hello: name});
- }, 1000);
- };
-
- node> async.dir(hello, 'world');
- {hello: 'world'}
-
+```js
+var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, {hello: name});
+ }, 1000);
+};
+```
+```js
+node> async.dir(hello, 'world');
+{hello: 'world'}
+```
---------------------------------------
+++ /dev/null
-// This file is just added for convenience so this repository can be
-// directly checked out into a project's deps folder
-module.exports = require('./lib/async');
-/*global setTimeout: false, console: false */
+/*global setImmediate: false, setTimeout: false, console: false */
(function () {
var async = {};
// global on the server, window in the browser
- var root = this,
- previous_async = root.async;
+ var root, previous_async;
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = async;
- }
- else {
- root.async = async;
+ root = this;
+ if (root != null) {
+ previous_async = root.async;
}
async.noConflict = function () {
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 _forEach = function (arr, iterator) {
+ var _each = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
return arr.map(iterator);
}
var results = [];
- _forEach(arr, function (x, i, a) {
+ _each(arr, function (x, i, a) {
results.push(iterator(x, i, a));
});
return results;
if (arr.reduce) {
return arr.reduce(iterator, memo);
}
- _forEach(arr, function (x, i, a) {
+ _each(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
//// nextTick implementation with browser-compatible fallback ////
if (typeof process === 'undefined' || !(process.nextTick)) {
- async.nextTick = function (fn) {
- setTimeout(fn, 0);
- };
+ if (typeof setImmediate === 'function') {
+ async.setImmediate = setImmediate;
+ async.nextTick = setImmediate;
+ }
+ else {
+ async.nextTick = function (fn) {
+ setTimeout(fn, 0);
+ };
+ async.setImmediate = async.nextTick;
+ }
}
else {
async.nextTick = process.nextTick;
+ if (typeof setImmediate !== 'undefined') {
+ async.setImmediate = setImmediate;
+ }
+ else {
+ async.setImmediate = async.nextTick;
+ }
}
- async.forEach = function (arr, iterator, callback) {
+ async.each = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
- _forEach(arr, function (x) {
- iterator(x, function (err) {
+ _each(arr, function (x) {
+ iterator(x, only_once(function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
- if (completed === arr.length) {
+ if (completed >= arr.length) {
callback(null);
}
}
- });
+ }));
});
};
+ async.forEach = async.each;
- async.forEachSeries = function (arr, iterator, callback) {
+ async.eachSeries = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
else {
completed += 1;
- if (completed === arr.length) {
+ if (completed >= arr.length) {
callback(null);
}
else {
};
iterate();
};
+ async.forEachSeries = async.eachSeries;
- async.forEachLimit = function (arr, limit, iterator, callback) {
- callback = callback || function () {};
- if (!arr.length || limit <= 0) {
- return callback();
- }
- var completed = 0;
- var started = 0;
- var running = 0;
+ 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) {
- (function replenish () {
- if (completed === arr.length) {
+ return function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length || limit <= 0) {
return callback();
}
+ var completed = 0;
+ var started = 0;
+ var running = 0;
- 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();
+ (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 {
- replenish();
+ 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.forEach].concat(args));
+ 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.forEachSeries].concat(args));
+ return fn.apply(null, [async.eachSeries].concat(args));
};
};
};
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.forEachSeries(arr, function (x, callback) {
+ async.eachSeries(arr, function (x, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
async.detectSeries = doSeries(_detect);
async.some = function (arr, iterator, main_callback) {
- async.forEach(arr, function (x, callback) {
+ async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (v) {
main_callback(true);
async.any = async.some;
async.every = function (arr, iterator, main_callback) {
- async.forEach(arr, function (x, callback) {
+ async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (!v) {
main_callback(false);
}
};
var taskComplete = function () {
- _forEach(listeners.slice(0), function (fn) {
+ _each(listeners.slice(0), function (fn) {
fn();
});
};
}
});
- _forEach(keys, function (k) {
+ _each(keys, function (k) {
var task = (tasks[k] instanceof Function) ? [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) {
- callback(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 {
- var args = Array.prototype.slice.call(arguments, 1);
- if (args.length <= 1) {
- args = args[0];
- }
results[k] = args;
- taskComplete();
+ async.setImmediate(taskComplete);
}
};
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
async.waterfall = function (tasks, callback) {
callback = callback || function () {};
+ if (tasks.constructor !== Array) {
+ 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(err);
+ callback.apply(null, arguments);
callback = function () {};
}
else {
else {
args.push(callback);
}
- async.nextTick(function () {
+ async.setImmediate(function () {
iterator.apply(null, args);
});
}
wrapIterator(async.iterator(tasks))();
};
- async.parallel = function (tasks, callback) {
+ var _parallel = function(eachfn, tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
- async.map(tasks, function (fn, callback) {
+ eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
}
else {
var results = {};
- async.forEach(_keys(tasks), function (k, callback) {
+ eachfn.each(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
}
};
+ 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 (tasks.constructor === Array) {
}
else {
var results = {};
- async.forEachSeries(_keys(tasks), function (k, callback) {
+ async.eachSeries(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
}
};
+ async.doWhilst = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ if (test()) {
+ async.doWhilst(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
async.until = function (test, iterator, callback) {
if (!test()) {
iterator(function (err) {
}
};
+ async.doUntil = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ if (!test()) {
+ async.doUntil(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
async.queue = function (worker, concurrency) {
+ if (concurrency === undefined) {
+ concurrency = 1;
+ }
+ function _insert(q, data, pos, callback) {
+ if(data.constructor !== Array) {
+ data = [data];
+ }
+ _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 === concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
var workers = 0;
var q = {
tasks: [],
empty: null,
drain: null,
push: function (data, callback) {
- if(data.constructor !== Array) {
- data = [data];
- }
- _forEach(data, function(task) {
- q.tasks.push({
- data: task,
- callback: typeof callback === 'function' ? callback : null
- });
- if (q.saturated && q.tasks.length == concurrency) {
- q.saturated();
- }
- async.nextTick(q.process);
- });
+ _insert(q, data, false, callback);
+ },
+ unshift: function (data, callback) {
+ _insert(q, data, true, callback);
},
process: function () {
if (workers < q.concurrency && q.tasks.length) {
var task = q.tasks.shift();
- if(q.empty && q.tasks.length == 0) q.empty();
+ if (q.empty && q.tasks.length === 0) {
+ q.empty();
+ }
workers += 1;
- worker(task.data, function () {
+ var next = function () {
workers -= 1;
if (task.callback) {
task.callback.apply(task, arguments);
}
- if(q.drain && q.tasks.length + workers == 0) q.drain();
+ 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;
};
+ async.cargo = function (worker, payload) {
+ var working = false,
+ tasks = [];
+
+ var cargo = {
+ tasks: tasks,
+ payload: payload,
+ saturated: null,
+ empty: null,
+ drain: null,
+ push: function (data, callback) {
+ if(data.constructor !== Array) {
+ data = [data];
+ }
+ _each(data, function(task) {
+ tasks.push({
+ data: task,
+ callback: typeof callback === 'function' ? callback : null
+ });
+ 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.drain();
+ return;
+ }
+
+ var ts = typeof payload === 'number'
+ ? tasks.splice(0, payload)
+ : tasks.splice(0);
+
+ 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);
}
}
else if (console[name]) {
- _forEach(args, function (x) {
+ _each(args, function (x) {
console[name](x);
});
}
}]));
}
};
+ memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
};
};
};
+ 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.compose = function (/* functions... */) {
+ var fns = Array.prototype.reverse.call(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));
+ });
+ };
+ };
+
+ 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();
+ };
+
+ // AMD / RequireJS
+ if (typeof define !== 'undefined' && define.amd) {
+ define([], function () {
+ return async;
+ });
+ }
+ // Node.js
+ else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = async;
+ }
+ // included directly via <script> tag
+ else {
+ root.async = async;
+ }
+
}());
{
"name": "async",
"description": "Higher-order functions and common patterns for asynchronous code",
- "main": "./index",
+ "main": "./lib/async",
"author": {
"name": "Caolan McMahon"
},
- "version": "0.1.22",
+ "version": "0.2.8",
"repository": {
"type": "git",
"url": "http://github.com/caolan/async.git"
"uglify-js": "1.2.x",
"nodelint": ">0.0.0"
},
- "readme": "# Async.js\n\nAsync is a utility module which provides straight-forward, powerful functions\nfor working with asynchronous JavaScript. Although originally designed for\nuse with [node.js](http://nodejs.org), it can also be used directly in the\nbrowser.\n\nAsync provides around 20 functions that include the usual 'functional'\nsuspects (map, reduce, filter, forEach…) as well as some common patterns\nfor asynchronous control flow (parallel, series, waterfall…). All these\nfunctions assume you follow the node.js convention of providing a single\ncallback as the last argument of your async function.\n\n\n## Quick Examples\n\n async.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n });\n\n async.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n });\n\n async.parallel([\n function(){ ... },\n function(){ ... }\n ], callback);\n\n async.series([\n function(){ ... },\n function(){ ... }\n ]);\n\nThere are many more functions available so take a look at the docs below for a\nfull list. This module aims to be comprehensive, so if you feel anything is\nmissing please create a GitHub issue for it.\n\n\n## Download\n\nReleases are available for download from\n[GitHub](http://github.com/caolan/async/downloads).\nAlternatively, you can install using Node Package Manager (npm):\n\n npm install async\n\n\n__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed\n\n__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped\n\n\n## In the Browser\n\nSo far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:\n\n <script type=\"text/javascript\" src=\"async.js\"></script>\n <script type=\"text/javascript\">\n\n async.map(data, asyncProcess, function(err, results){\n alert(results);\n });\n\n </script>\n\n\n## Documentation\n\n### Collections\n\n* [forEach](#forEach)\n* [map](#map)\n* [filter](#filter)\n* [reject](#reject)\n* [reduce](#reduce)\n* [detect](#detect)\n* [sortBy](#sortBy)\n* [some](#some)\n* [every](#every)\n* [concat](#concat)\n\n### Control Flow\n\n* [series](#series)\n* [parallel](#parallel)\n* [whilst](#whilst)\n* [until](#until)\n* [waterfall](#waterfall)\n* [queue](#queue)\n* [auto](#auto)\n* [iterator](#iterator)\n* [apply](#apply)\n* [nextTick](#nextTick)\n\n### Utils\n\n* [memoize](#memoize)\n* [unmemoize](#unmemoize)\n* [log](#log)\n* [dir](#dir)\n* [noConflict](#noConflict)\n\n\n## Collections\n\n<a name=\"forEach\" />\n### forEach(arr, iterator, callback)\n\nApplies an iterator function to each item in an array, in parallel.\nThe iterator is called with an item from the list and a callback for when it\nhas finished. If the iterator passes an error to this callback, the main\ncallback for the forEach function is immediately called with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n // assuming openFiles is an array of file names and saveFile is a function\n // to save the modified contents of that file:\n\n async.forEach(openFiles, saveFile, function(err){\n // if any of the saves produced an error, err would equal that error\n });\n\n---------------------------------------\n\n<a name=\"forEachSeries\" />\n### forEachSeries(arr, iterator, callback)\n\nThe same as forEach only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. This means the iterator functions will complete in order.\n\n\n---------------------------------------\n\n<a name=\"forEachLimit\" />\n### forEachLimit(arr, limit, iterator, callback)\n\nThe same as forEach only the iterator is applied to batches of items in the\narray, in series. The next batch of iterators is only called once the current\none has completed processing.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - How many items should be in each batch.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n // Assume documents is an array of JSON objects and requestApi is a\n // function that interacts with a rate-limited REST api.\n\n async.forEachLimit(documents, 20, requestApi, function(err){\n // if any of the saves produced an error, err would equal that error\n });\n---------------------------------------\n\n<a name=\"map\" />\n### map(arr, iterator, callback)\n\nProduces a new array of values by mapping each value in the given array through\nthe iterator function. The iterator is called with an item from the array and a\ncallback for when it has finished processing. The callback takes 2 arguments, \nan error and the transformed item from the array. If the iterator passes an\nerror to this callback, the main callback for the map function is immediately\ncalled with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order, however\nthe results array will be in the same order as the original array.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n async.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n });\n\n---------------------------------------\n\n<a name=\"mapSeries\" />\n### mapSeries(arr, iterator, callback)\n\nThe same as map only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n---------------------------------------\n\n<a name=\"filter\" />\n### filter(arr, iterator, callback)\n\n__Alias:__ select\n\nReturns a new array of all the values which pass an async truth test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. This operation is\nperformed in parallel, but the results array will be in the same order as the\noriginal.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(results) - A callback which is called after all the iterator\n functions have finished.\n\n__Example__\n\n async.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n });\n\n---------------------------------------\n\n<a name=\"filterSeries\" />\n### filterSeries(arr, iterator, callback)\n\n__alias:__ selectSeries\n\nThe same as filter only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n---------------------------------------\n\n<a name=\"reject\" />\n### reject(arr, iterator, callback)\n\nThe opposite of filter. Removes values that pass an async truth test.\n\n---------------------------------------\n\n<a name=\"rejectSeries\" />\n### rejectSeries(arr, iterator, callback)\n\nThe same as filter, only the iterator is applied to each item in the array\nin series.\n\n\n---------------------------------------\n\n<a name=\"reduce\" />\n### reduce(arr, memo, iterator, callback)\n\n__aliases:__ inject, foldl\n\nReduces a list of values into a single value using an async iterator to return\neach successive step. Memo is the initial state of the reduction. This\nfunction only operates in series. For performance reasons, it may make sense to\nsplit a call to this function into a parallel map, then use the normal\nArray.prototype.reduce on the results. This function is for situations where\neach step in the reduction needs to be async, if you can get the data before\nreducing it then its probably a good idea to do so.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* memo - The initial state of the reduction.\n* iterator(memo, item, callback) - A function applied to each item in the\n array to produce the next step in the reduction. The iterator is passed a\n callback which accepts an optional error as its first argument, and the state\n of the reduction as the second. If an error is passed to the callback, the\n reduction is stopped and the main callback is immediately called with the\n error.\n* callback(err, result) - A callback which is called after all the iterator\n functions have finished. Result is the reduced value.\n\n__Example__\n\n async.reduce([1,2,3], 0, function(memo, item, callback){\n // pointless async:\n process.nextTick(function(){\n callback(null, memo + item)\n });\n }, function(err, result){\n // result is now equal to the last value of memo, which is 6\n });\n\n---------------------------------------\n\n<a name=\"reduceRight\" />\n### reduceRight(arr, memo, iterator, callback)\n\n__Alias:__ foldr\n\nSame as reduce, only operates on the items in the array in reverse order.\n\n\n---------------------------------------\n\n<a name=\"detect\" />\n### detect(arr, iterator, callback)\n\nReturns the first value in a list that passes an async truth test. The\niterator is applied in parallel, meaning the first iterator to return true will\nfire the detect callback with that result. That means the result might not be\nthe first item in the original array (in terms of order) that passes the test.\n\nIf order within the original array is important then look at detectSeries.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n the first item in the array that passes the truth test (iterator) or the\n value undefined if none passed.\n\n__Example__\n\n async.detect(['file1','file2','file3'], path.exists, function(result){\n // result now equals the first file in the list that exists\n });\n\n---------------------------------------\n\n<a name=\"detectSeries\" />\n### detectSeries(arr, iterator, callback)\n\nThe same as detect, only the iterator is applied to each item in the array\nin series. This means the result is always the first in the original array (in\nterms of array order) that passes the truth test.\n\n\n---------------------------------------\n\n<a name=\"sortBy\" />\n### sortBy(arr, iterator, callback)\n\nSorts a list by the results of running each value through an async iterator.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and a value to use as the sort criteria.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is the items from\n the original array sorted by the values returned by the iterator calls.\n\n__Example__\n\n async.sortBy(['file1','file2','file3'], function(file, callback){\n fs.stat(file, function(err, stats){\n callback(err, stats.mtime);\n });\n }, function(err, results){\n // results is now the original array of files sorted by\n // modified date\n });\n\n\n---------------------------------------\n\n<a name=\"some\" />\n### some(arr, iterator, callback)\n\n__Alias:__ any\n\nReturns true if at least one element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. Once any iterator\ncall returns true, the main callback is immediately called.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n either true or false depending on the values of the async tests.\n\n__Example__\n\n async.some(['file1','file2','file3'], path.exists, function(result){\n // if result is true then at least one of the files exists\n });\n\n---------------------------------------\n\n<a name=\"every\" />\n### every(arr, iterator, callback)\n\n__Alias:__ all\n\nReturns true if every element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called after all the iterator\n functions have finished. Result will be either true or false depending on\n the values of the async tests.\n\n__Example__\n\n async.every(['file1','file2','file3'], path.exists, function(result){\n // if result is true then every file exists\n });\n\n---------------------------------------\n\n<a name=\"concat\" />\n### concat(arr, iterator, callback)\n\nApplies an iterator to each item in a list, concatenating the results. Returns the\nconcatenated list. The iterators are called in parallel, and the results are\nconcatenated as they return. There is no guarantee that the results array will\nbe returned in the original order of the arguments passed to the iterator function.\n\n__Arguments__\n\n* arr - An array to iterate over\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and an array of results.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array containing\n the concatenated results of the iterator function.\n\n__Example__\n\n async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){\n // files is now a list of filenames that exist in the 3 directories\n });\n\n---------------------------------------\n\n<a name=\"concatSeries\" />\n### concatSeries(arr, iterator, callback)\n\nSame as async.concat, but executes in series instead of parallel.\n\n\n## Control Flow\n\n<a name=\"series\" />\n### series(tasks, [callback])\n\nRun an array of functions in series, each one running once the previous\nfunction has completed. If any functions in the series pass an error to its\ncallback, no more functions are run and the callback for the series is\nimmediately called with the value of the error. Once the tasks have completed,\nthe results are passed to the final callback as an array.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.series.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed\n a callback it must call on completion.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets an array of all the arguments passed to\n the callbacks used in the array.\n\n__Example__\n\n async.series([\n function(callback){\n // do some stuff ...\n callback(null, 'one');\n },\n function(callback){\n // do some more stuff ...\n callback(null, 'two');\n },\n ],\n // optional callback\n function(err, results){\n // results is now equal to ['one', 'two']\n });\n\n\n // an example using an object instead of an array\n async.series({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n },\n },\n function(err, results) {\n // results is now equal to: {one: 1, two: 2}\n });\n\n\n---------------------------------------\n\n<a name=\"parallel\" />\n### parallel(tasks, [callback])\n\nRun an array of functions in parallel, without waiting until the previous\nfunction has completed. If any of the functions pass an error to its\ncallback, the main callback is immediately called with the value of the error.\nOnce the tasks have completed, the results are passed to the final callback as an\narray.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.parallel.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed a\n callback it must call on completion.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets an array of all the arguments passed to\n the callbacks used in the array.\n\n__Example__\n\n async.parallel([\n function(callback){\n setTimeout(function(){\n callback(null, 'one');\n }, 200);\n },\n function(callback){\n setTimeout(function(){\n callback(null, 'two');\n }, 100);\n },\n ],\n // optional callback\n function(err, results){\n // the results array will equal ['one','two'] even though\n // the second function had a shorter timeout.\n });\n\n\n // an example using an object instead of an array\n async.parallel({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n },\n },\n function(err, results) {\n // results is now equals to: {one: 1, two: 2}\n });\n\n\n---------------------------------------\n\n<a name=\"whilst\" />\n### whilst(test, fn, callback)\n\nRepeatedly call fn, while test returns true. Calls the callback when stopped,\nor an error occurs.\n\n__Arguments__\n\n* test() - synchronous truth test to perform before each execution of fn.\n* fn(callback) - A function to call each time the test passes. The function is\n passed a callback which must be called once it has completed with an optional\n error as the first argument.\n* callback(err) - A callback which is called after the test fails and repeated\n execution of fn has stopped.\n\n__Example__\n\n var count = 0;\n\n async.whilst(\n function () { return count < 5; },\n function (callback) {\n count++;\n setTimeout(callback, 1000);\n },\n function (err) {\n // 5 seconds have passed\n }\n );\n\n\n---------------------------------------\n\n<a name=\"until\" />\n### until(test, fn, callback)\n\nRepeatedly call fn, until test returns true. Calls the callback when stopped,\nor an error occurs.\n\nThe inverse of async.whilst.\n\n\n---------------------------------------\n\n<a name=\"waterfall\" />\n### waterfall(tasks, [callback])\n\nRuns an array of functions in series, each passing their results to the next in\nthe array. However, if any of the functions pass an error to the callback, the\nnext function is not executed and the main callback is immediately called with\nthe error.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a callback it\n must call on completion.\n* callback(err, [results]) - An optional callback to run once all the functions\n have completed. This will be passed the results of the last task's callback.\n\n\n\n__Example__\n\n async.waterfall([\n function(callback){\n callback(null, 'one', 'two');\n },\n function(arg1, arg2, callback){\n callback(null, 'three');\n },\n function(arg1, callback){\n // arg1 now equals 'three'\n callback(null, 'done');\n }\n ], function (err, result) {\n // result now equals 'done' \n });\n\n\n---------------------------------------\n\n<a name=\"queue\" />\n### queue(worker, concurrency)\n\nCreates a queue object with the specified concurrency. Tasks added to the\nqueue will be processed in parallel (up to the concurrency limit). If all\nworkers are in progress, the task is queued until one is available. Once\na worker has completed a task, the task's callback is called.\n\n__Arguments__\n\n* worker(task, callback) - An asynchronous function for processing a queued\n task.\n* concurrency - An integer for determining how many worker functions should be\n run in parallel.\n\n__Queue objects__\n\nThe queue object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* concurrency - an integer for determining how many worker functions should be\n run in parallel. This property can be changed after a queue is created to\n alter the concurrency on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n // create a queue object with concurrency 2\n\n var q = async.queue(function (task, callback) {\n console.log('hello ' + task.name);\n callback();\n }, 2);\n\n\n // assign a callback\n q.drain = function() {\n console.log('all items have been processed');\n }\n\n // add some items to the queue\n\n q.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n });\n q.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n });\n\n // add some items to the queue (batch-wise)\n\n q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {\n console.log('finished processing bar');\n });\n\n\n---------------------------------------\n\n<a name=\"auto\" />\n### auto(tasks, [callback])\n\nDetermines the best order for running functions based on their requirements.\nEach function can optionally depend on other functions being completed first,\nand each function is run as soon as its requirements are satisfied. If any of\nthe functions pass an error to their callback, that function will not complete\n(so any other functions depending on it will not run) and the main callback\nwill be called immediately with the error. Functions also receive an object\ncontaining the results of functions which have completed so far.\n\n__Arguments__\n\n* tasks - An object literal containing named functions or an array of\n requirements, with the function itself the last item in the array. The key\n used for each function or array is used when specifying requirements. The\n syntax is easier to understand by looking at the example.\n* callback(err, results) - An optional callback which is called when all the\n tasks have been completed. The callback will receive an error as an argument\n if any tasks pass an error to their callback. If all tasks complete\n successfully, it will receive an object containing their results.\n\n__Example__\n\n async.auto({\n get_data: function(callback){\n // async code to get some data\n },\n make_folder: function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n },\n write_file: ['get_data', 'make_folder', function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n callback(null, filename);\n }],\n email_link: ['write_file', function(callback, results){\n // once the file is written let's email a link to it...\n // results.write_file contains the filename returned by write_file.\n }]\n });\n\nThis is a fairly trivial example, but to do this using the basic parallel and\nseries functions would look like this:\n\n async.parallel([\n function(callback){\n // async code to get some data\n },\n function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n }\n ],\n function(results){\n async.series([\n function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n },\n email_link: function(callback){\n // once the file is written let's email a link to it...\n }\n ]);\n });\n\nFor a complicated series of async tasks using the auto function makes adding\nnew tasks much easier and makes the code more readable.\n\n\n---------------------------------------\n\n<a name=\"iterator\" />\n### iterator(tasks)\n\nCreates an iterator function which calls the next function in the array,\nreturning a continuation to call the next one after that. Its also possible to\n'peek' the next iterator by doing iterator.next().\n\nThis function is used internally by the async module but can be useful when\nyou want to manually control the flow of functions in series.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a callback it\n must call on completion.\n\n__Example__\n\n var iterator = async.iterator([\n function(){ sys.p('one'); },\n function(){ sys.p('two'); },\n function(){ sys.p('three'); }\n ]);\n\n node> var iterator2 = iterator();\n 'one'\n node> var iterator3 = iterator2();\n 'two'\n node> iterator3();\n 'three'\n node> var nextfn = iterator2.next();\n node> nextfn();\n 'three'\n\n\n---------------------------------------\n\n<a name=\"apply\" />\n### apply(function, arguments..)\n\nCreates a continuation function with some arguments already applied, a useful\nshorthand when combined with other control flow functions. Any arguments\npassed to the returned function are added to the arguments originally passed\nto apply.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to automatically apply when the\n continuation is called.\n\n__Example__\n\n // using apply\n\n async.parallel([\n async.apply(fs.writeFile, 'testfile1', 'test1'),\n async.apply(fs.writeFile, 'testfile2', 'test2'),\n ]);\n\n\n // the same process without using apply\n\n async.parallel([\n function(callback){\n fs.writeFile('testfile1', 'test1', callback);\n },\n function(callback){\n fs.writeFile('testfile2', 'test2', callback);\n },\n ]);\n\nIt's possible to pass any number of additional arguments when calling the\ncontinuation:\n\n node> var fn = async.apply(sys.puts, 'one');\n node> fn('two', 'three');\n one\n two\n three\n\n---------------------------------------\n\n<a name=\"nextTick\" />\n### nextTick(callback)\n\nCalls the callback on a later loop around the event loop. In node.js this just\ncalls process.nextTick, in the browser it falls back to setTimeout(callback, 0),\nwhich means other higher priority events may precede the execution of the callback.\n\nThis is used internally for browser-compatibility purposes.\n\n__Arguments__\n\n* callback - The function to call on a later loop around the event loop.\n\n__Example__\n\n var call_order = [];\n async.nextTick(function(){\n call_order.push('two');\n // call_order now equals ['one','two]\n });\n call_order.push('one')\n\n\n## Utils\n\n<a name=\"memoize\" />\n### memoize(fn, [hasher])\n\nCaches the results of an async function. When creating a hash to store function\nresults against, the callback is omitted from the hash and an optional hash\nfunction can be used.\n\n__Arguments__\n\n* fn - the function you to proxy and cache results from.\n* hasher - an optional function for generating a custom hash for storing\n results, it has all the arguments applied to it apart from the callback, and\n must be synchronous.\n\n__Example__\n\n var slow_fn = function (name, callback) {\n // do something\n callback(null, result);\n };\n var fn = async.memoize(slow_fn);\n\n // fn can now be used as if it were slow_fn\n fn('some name', function () {\n // callback\n });\n\n<a name=\"unmemoize\" />\n### unmemoize(fn)\n\nUndoes a memoized function, reverting it to the original, unmemoized\nform. Comes handy in tests.\n\n__Arguments__\n\n* fn - the memoized function\n\n<a name=\"log\" />\n### log(function, arguments)\n\nLogs the result of an async function to the console. Only works in node.js or\nin browsers that support console.log and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.log is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n var hello = function(name, callback){\n setTimeout(function(){\n callback(null, 'hello ' + name);\n }, 1000);\n };\n\n node> async.log(hello, 'world');\n 'hello world'\n\n\n---------------------------------------\n\n<a name=\"dir\" />\n### dir(function, arguments)\n\nLogs the result of an async function to the console using console.dir to\ndisplay the properties of the resulting object. Only works in node.js or\nin browsers that support console.dir and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.dir is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n var hello = function(name, callback){\n setTimeout(function(){\n callback(null, {hello: name});\n }, 1000);\n };\n\n node> async.dir(hello, 'world');\n {hello: 'world'}\n\n\n---------------------------------------\n\n<a name=\"noConflict\" />\n### noConflict()\n\nChanges the value of async back to its original value, returning a reference to the\nasync object.\n",
- "readmeFilename": "README.md",
- "_id": "async@0.1.22",
- "dist": {
- "shasum": "9410ff209b32d640cd9397f25fb77c2c634d60cc"
+ "jam": {
+ "main": "lib/async.js",
+ "include": [
+ "lib/async.js",
+ "README.md",
+ "LICENSE"
+ ]
+ },
+ "scripts": {
+ "test": "nodeunit test/test-async.js"
},
- "_from": "async@~0.1.9",
- "_resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ "readme": "# Async.js\n\nAsync is a utility module which provides straight-forward, powerful functions\nfor working with asynchronous JavaScript. Although originally designed for\nuse with [node.js](http://nodejs.org), it can also be used directly in the\nbrowser.\n\nAsync provides around 20 functions that include the usual 'functional'\nsuspects (map, reduce, filter, each…) as well as some common patterns\nfor asynchronous control flow (parallel, series, waterfall…). All these\nfunctions assume you follow the node.js convention of providing a single\ncallback as the last argument of your async function.\n\n\n## Quick Examples\n\n```javascript\nasync.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n\nasync.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n});\n\nasync.parallel([\n function(){ ... },\n function(){ ... }\n], callback);\n\nasync.series([\n function(){ ... },\n function(){ ... }\n]);\n```\n\nThere are many more functions available so take a look at the docs below for a\nfull list. This module aims to be comprehensive, so if you feel anything is\nmissing please create a GitHub issue for it.\n\n## Common Pitfalls\n\n### Binding a context to an iterator\n\nThis section is really about bind, not about async. If you are wondering how to\nmake async execute your iterators in a given context, or are confused as to why\na method of another library isn't working as an iterator, study this example:\n\n```js\n// Here is a simple object with an (unnecessarily roundabout) squaring method\nvar AsyncSquaringLibrary = {\n squareExponent: 2,\n square: function(number, callback){ \n var result = Math.pow(number, this.squareExponent);\n setTimeout(function(){\n callback(null, result);\n }, 200);\n }\n};\n\nasync.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){\n // result is [NaN, NaN, NaN]\n // This fails because the `this.squareExponent` expression in the square\n // function is not evaluated in the context of AsyncSquaringLibrary, and is\n // therefore undefined.\n});\n\nasync.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){\n // result is [1, 4, 9]\n // With the help of bind we can attach a context to the iterator before\n // passing it to async. Now the square function will be executed in its \n // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`\n // will be as expected.\n});\n```\n\n## Download\n\nThe source is available for download from\n[GitHub](http://github.com/caolan/async).\nAlternatively, you can install using Node Package Manager (npm):\n\n npm install async\n\n__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed\n\n## In the Browser\n\nSo far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:\n\n```html\n<script type=\"text/javascript\" src=\"async.js\"></script>\n<script type=\"text/javascript\">\n\n async.map(data, asyncProcess, function(err, results){\n alert(results);\n });\n\n</script>\n```\n\n## Documentation\n\n### Collections\n\n* [each](#each)\n* [map](#map)\n* [filter](#filter)\n* [reject](#reject)\n* [reduce](#reduce)\n* [detect](#detect)\n* [sortBy](#sortBy)\n* [some](#some)\n* [every](#every)\n* [concat](#concat)\n\n### Control Flow\n\n* [series](#series)\n* [parallel](#parallel)\n* [whilst](#whilst)\n* [doWhilst](#doWhilst)\n* [until](#until)\n* [doUntil](#doUntil)\n* [forever](#forever)\n* [waterfall](#waterfall)\n* [compose](#compose)\n* [applyEach](#applyEach)\n* [queue](#queue)\n* [cargo](#cargo)\n* [auto](#auto)\n* [iterator](#iterator)\n* [apply](#apply)\n* [nextTick](#nextTick)\n* [times](#times)\n* [timesSeries](#timesSeries)\n\n### Utils\n\n* [memoize](#memoize)\n* [unmemoize](#unmemoize)\n* [log](#log)\n* [dir](#dir)\n* [noConflict](#noConflict)\n\n\n## Collections\n\n<a name=\"forEach\" />\n<a name=\"each\" />\n### each(arr, iterator, callback)\n\nApplies an iterator function to each item in an array, in parallel.\nThe iterator is called with an item from the list and a callback for when it\nhas finished. If the iterator passes an error to this callback, the main\ncallback for the each function is immediately called with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err) which must be called once it has \n completed. If no error has occured, the callback should be run without \n arguments or with an explicit null argument.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n```js\n// assuming openFiles is an array of file names and saveFile is a function\n// to save the modified contents of that file:\n\nasync.each(openFiles, saveFile, function(err){\n // if any of the saves produced an error, err would equal that error\n});\n```\n\n---------------------------------------\n\n<a name=\"forEachSeries\" />\n<a name=\"eachSeries\" />\n### eachSeries(arr, iterator, callback)\n\nThe same as each only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. This means the iterator functions will complete in order.\n\n\n---------------------------------------\n\n<a name=\"forEachLimit\" />\n<a name=\"eachLimit\" />\n### eachLimit(arr, limit, iterator, callback)\n\nThe same as each only no more than \"limit\" iterators will be simultaneously \nrunning at any time.\n\nNote that the items are not processed in batches, so there is no guarantee that\n the first \"limit\" iterator functions will complete before any others are \nstarted.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - The maximum number of iterators to run at any time.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err) which must be called once it has \n completed. If no error has occured, the callback should be run without \n arguments or with an explicit null argument.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n```js\n// Assume documents is an array of JSON objects and requestApi is a\n// function that interacts with a rate-limited REST api.\n\nasync.eachLimit(documents, 20, requestApi, function(err){\n // if any of the saves produced an error, err would equal that error\n});\n```\n\n---------------------------------------\n\n<a name=\"map\" />\n### map(arr, iterator, callback)\n\nProduces a new array of values by mapping each value in the given array through\nthe iterator function. The iterator is called with an item from the array and a\ncallback for when it has finished processing. The callback takes 2 arguments, \nan error and the transformed item from the array. If the iterator passes an\nerror to this callback, the main callback for the map function is immediately\ncalled with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order, however\nthe results array will be in the same order as the original array.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, transformed) which must be called once \n it has completed with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n```js\nasync.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n```\n\n---------------------------------------\n\n<a name=\"mapSeries\" />\n### mapSeries(arr, iterator, callback)\n\nThe same as map only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n---------------------------------------\n\n<a name=\"mapLimit\" />\n### mapLimit(arr, limit, iterator, callback)\n\nThe same as map only no more than \"limit\" iterators will be simultaneously \nrunning at any time.\n\nNote that the items are not processed in batches, so there is no guarantee that\n the first \"limit\" iterator functions will complete before any others are \nstarted.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - The maximum number of iterators to run at any time.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, transformed) which must be called once \n it has completed with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n```js\nasync.map(['file1','file2','file3'], 1, fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n```\n\n---------------------------------------\n\n<a name=\"filter\" />\n### filter(arr, iterator, callback)\n\n__Alias:__ select\n\nReturns a new array of all the values which pass an async truth test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. This operation is\nperformed in parallel, but the results array will be in the same order as the\noriginal.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(results) - A callback which is called after all the iterator\n functions have finished.\n\n__Example__\n\n```js\nasync.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n});\n```\n\n---------------------------------------\n\n<a name=\"filterSeries\" />\n### filterSeries(arr, iterator, callback)\n\n__alias:__ selectSeries\n\nThe same as filter only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n---------------------------------------\n\n<a name=\"reject\" />\n### reject(arr, iterator, callback)\n\nThe opposite of filter. Removes values that pass an async truth test.\n\n---------------------------------------\n\n<a name=\"rejectSeries\" />\n### rejectSeries(arr, iterator, callback)\n\nThe same as reject, only the iterator is applied to each item in the array\nin series.\n\n\n---------------------------------------\n\n<a name=\"reduce\" />\n### reduce(arr, memo, iterator, callback)\n\n__aliases:__ inject, foldl\n\nReduces a list of values into a single value using an async iterator to return\neach successive step. Memo is the initial state of the reduction. This\nfunction only operates in series. For performance reasons, it may make sense to\nsplit a call to this function into a parallel map, then use the normal\nArray.prototype.reduce on the results. This function is for situations where\neach step in the reduction needs to be async, if you can get the data before\nreducing it then it's probably a good idea to do so.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* memo - The initial state of the reduction.\n* iterator(memo, item, callback) - A function applied to each item in the\n array to produce the next step in the reduction. The iterator is passed a\n callback(err, reduction) which accepts an optional error as its first \n argument, and the state of the reduction as the second. If an error is \n passed to the callback, the reduction is stopped and the main callback is \n immediately called with the error.\n* callback(err, result) - A callback which is called after all the iterator\n functions have finished. Result is the reduced value.\n\n__Example__\n\n```js\nasync.reduce([1,2,3], 0, function(memo, item, callback){\n // pointless async:\n process.nextTick(function(){\n callback(null, memo + item)\n });\n}, function(err, result){\n // result is now equal to the last value of memo, which is 6\n});\n```\n\n---------------------------------------\n\n<a name=\"reduceRight\" />\n### reduceRight(arr, memo, iterator, callback)\n\n__Alias:__ foldr\n\nSame as reduce, only operates on the items in the array in reverse order.\n\n\n---------------------------------------\n\n<a name=\"detect\" />\n### detect(arr, iterator, callback)\n\nReturns the first value in a list that passes an async truth test. The\niterator is applied in parallel, meaning the first iterator to return true will\nfire the detect callback with that result. That means the result might not be\nthe first item in the original array (in terms of order) that passes the test.\n\nIf order within the original array is important then look at detectSeries.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n the first item in the array that passes the truth test (iterator) or the\n value undefined if none passed.\n\n__Example__\n\n```js\nasync.detect(['file1','file2','file3'], path.exists, function(result){\n // result now equals the first file in the list that exists\n});\n```\n\n---------------------------------------\n\n<a name=\"detectSeries\" />\n### detectSeries(arr, iterator, callback)\n\nThe same as detect, only the iterator is applied to each item in the array\nin series. This means the result is always the first in the original array (in\nterms of array order) that passes the truth test.\n\n\n---------------------------------------\n\n<a name=\"sortBy\" />\n### sortBy(arr, iterator, callback)\n\nSorts a list by the results of running each value through an async iterator.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, sortValue) which must be called once it\n has completed with an error (which can be null) and a value to use as the sort\n criteria.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is the items from\n the original array sorted by the values returned by the iterator calls.\n\n__Example__\n\n```js\nasync.sortBy(['file1','file2','file3'], function(file, callback){\n fs.stat(file, function(err, stats){\n callback(err, stats.mtime);\n });\n}, function(err, results){\n // results is now the original array of files sorted by\n // modified date\n});\n```\n\n---------------------------------------\n\n<a name=\"some\" />\n### some(arr, iterator, callback)\n\n__Alias:__ any\n\nReturns true if at least one element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. Once any iterator\ncall returns true, the main callback is immediately called.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n either true or false depending on the values of the async tests.\n\n__Example__\n\n```js\nasync.some(['file1','file2','file3'], path.exists, function(result){\n // if result is true then at least one of the files exists\n});\n```\n\n---------------------------------------\n\n<a name=\"every\" />\n### every(arr, iterator, callback)\n\n__Alias:__ all\n\nReturns true if every element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(result) - A callback which is called after all the iterator\n functions have finished. Result will be either true or false depending on\n the values of the async tests.\n\n__Example__\n\n```js\nasync.every(['file1','file2','file3'], path.exists, function(result){\n // if result is true then every file exists\n});\n```\n\n---------------------------------------\n\n<a name=\"concat\" />\n### concat(arr, iterator, callback)\n\nApplies an iterator to each item in a list, concatenating the results. Returns the\nconcatenated list. The iterators are called in parallel, and the results are\nconcatenated as they return. There is no guarantee that the results array will\nbe returned in the original order of the arguments passed to the iterator function.\n\n__Arguments__\n\n* arr - An array to iterate over\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, results) which must be called once it \n has completed with an error (which can be null) and an array of results.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array containing\n the concatenated results of the iterator function.\n\n__Example__\n\n```js\nasync.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){\n // files is now a list of filenames that exist in the 3 directories\n});\n```\n\n---------------------------------------\n\n<a name=\"concatSeries\" />\n### concatSeries(arr, iterator, callback)\n\nSame as async.concat, but executes in series instead of parallel.\n\n\n## Control Flow\n\n<a name=\"series\" />\n### series(tasks, [callback])\n\nRun an array of functions in series, each one running once the previous\nfunction has completed. If any functions in the series pass an error to its\ncallback, no more functions are run and the callback for the series is\nimmediately called with the value of the error. Once the tasks have completed,\nthe results are passed to the final callback as an array.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.series.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed\n a callback(err, result) it must call on completion with an error (which can\n be null) and an optional result value.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n__Example__\n\n```js\nasync.series([\n function(callback){\n // do some stuff ...\n callback(null, 'one');\n },\n function(callback){\n // do some more stuff ...\n callback(null, 'two');\n }\n],\n// optional callback\nfunction(err, results){\n // results is now equal to ['one', 'two']\n});\n\n\n// an example using an object instead of an array\nasync.series({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n }\n},\nfunction(err, results) {\n // results is now equal to: {one: 1, two: 2}\n});\n```\n\n---------------------------------------\n\n<a name=\"parallel\" />\n### parallel(tasks, [callback])\n\nRun an array of functions in parallel, without waiting until the previous\nfunction has completed. If any of the functions pass an error to its\ncallback, the main callback is immediately called with the value of the error.\nOnce the tasks have completed, the results are passed to the final callback as an\narray.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.parallel.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed \n a callback(err, result) it must call on completion with an error (which can\n be null) and an optional result value.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n__Example__\n\n```js\nasync.parallel([\n function(callback){\n setTimeout(function(){\n callback(null, 'one');\n }, 200);\n },\n function(callback){\n setTimeout(function(){\n callback(null, 'two');\n }, 100);\n }\n],\n// optional callback\nfunction(err, results){\n // the results array will equal ['one','two'] even though\n // the second function had a shorter timeout.\n});\n\n\n// an example using an object instead of an array\nasync.parallel({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n }\n},\nfunction(err, results) {\n // results is now equals to: {one: 1, two: 2}\n});\n```\n\n---------------------------------------\n\n<a name=\"parallel\" />\n### parallelLimit(tasks, limit, [callback])\n\nThe same as parallel only the tasks are executed in parallel with a maximum of \"limit\" \ntasks executing at any time.\n\nNote that the tasks are not executed in batches, so there is no guarantee that \nthe first \"limit\" tasks will complete before any others are started.\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed \n a callback(err, result) it must call on completion with an error (which can\n be null) and an optional result value.\n* limit - The maximum number of tasks to run at any time.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n---------------------------------------\n\n<a name=\"whilst\" />\n### whilst(test, fn, callback)\n\nRepeatedly call fn, while test returns true. Calls the callback when stopped,\nor an error occurs.\n\n__Arguments__\n\n* test() - synchronous truth test to perform before each execution of fn.\n* fn(callback) - A function to call each time the test passes. The function is\n passed a callback(err) which must be called once it has completed with an \n optional error argument.\n* callback(err) - A callback which is called after the test fails and repeated\n execution of fn has stopped.\n\n__Example__\n\n```js\nvar count = 0;\n\nasync.whilst(\n function () { return count < 5; },\n function (callback) {\n count++;\n setTimeout(callback, 1000);\n },\n function (err) {\n // 5 seconds have passed\n }\n);\n```\n\n---------------------------------------\n\n<a name=\"doWhilst\" />\n### doWhilst(fn, test, callback)\n\nThe post check version of whilst. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.\n\n---------------------------------------\n\n<a name=\"until\" />\n### until(test, fn, callback)\n\nRepeatedly call fn, until test returns true. Calls the callback when stopped,\nor an error occurs.\n\nThe inverse of async.whilst.\n\n---------------------------------------\n\n<a name=\"doUntil\" />\n### doUntil(fn, test, callback)\n\nLike doWhilst except the test is inverted. Note the argument ordering differs from `until`.\n\n---------------------------------------\n\n<a name=\"forever\" />\n### forever(fn, callback)\n\nCalls the asynchronous function 'fn' repeatedly, in series, indefinitely.\nIf an error is passed to fn's callback then 'callback' is called with the\nerror, otherwise it will never be called.\n\n---------------------------------------\n\n<a name=\"waterfall\" />\n### waterfall(tasks, [callback])\n\nRuns an array of functions in series, each passing their results to the next in\nthe array. However, if any of the functions pass an error to the callback, the\nnext function is not executed and the main callback is immediately called with\nthe error.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a \n callback(err, result1, result2, ...) it must call on completion. The first\n argument is an error (which can be null) and any further arguments will be \n passed as arguments in order to the next task.\n* callback(err, [results]) - An optional callback to run once all the functions\n have completed. This will be passed the results of the last task's callback.\n\n\n\n__Example__\n\n```js\nasync.waterfall([\n function(callback){\n callback(null, 'one', 'two');\n },\n function(arg1, arg2, callback){\n callback(null, 'three');\n },\n function(arg1, callback){\n // arg1 now equals 'three'\n callback(null, 'done');\n }\n], function (err, result) {\n // result now equals 'done' \n});\n```\n\n---------------------------------------\n<a name=\"compose\" />\n### compose(fn1, fn2...)\n\nCreates a function which is a composition of the passed asynchronous\nfunctions. Each function consumes the return value of the function that\nfollows. Composing functions f(), g() and h() would produce the result of\nf(g(h())), only this version uses callbacks to obtain the return values.\n\nEach function is executed with the `this` binding of the composed function.\n\n__Arguments__\n\n* functions... - the asynchronous functions to compose\n\n\n__Example__\n\n```js\nfunction add1(n, callback) {\n setTimeout(function () {\n callback(null, n + 1);\n }, 10);\n}\n\nfunction mul3(n, callback) {\n setTimeout(function () {\n callback(null, n * 3);\n }, 10);\n}\n\nvar add1mul3 = async.compose(mul3, add1);\n\nadd1mul3(4, function (err, result) {\n // result now equals 15\n});\n```\n\n---------------------------------------\n<a name=\"applyEach\" />\n### applyEach(fns, args..., callback)\n\nApplies the provided arguments to each function in the array, calling the\ncallback after all functions have completed. If you only provide the first\nargument then it will return a function which lets you pass in the\narguments as if it were a single function call.\n\n__Arguments__\n\n* fns - the asynchronous functions to all call with the same arguments\n* args... - any number of separate arguments to pass to the function\n* callback - the final argument should be the callback, called when all\n functions have completed processing\n\n\n__Example__\n\n```js\nasync.applyEach([enableSearch, updateSchema], 'bucket', callback);\n\n// partial application example:\nasync.each(\n buckets,\n async.applyEach([enableSearch, updateSchema]),\n callback\n);\n```\n\n---------------------------------------\n\n<a name=\"applyEachSeries\" />\n### applyEachSeries(arr, iterator, callback)\n\nThe same as applyEach only the functions are applied in series.\n\n---------------------------------------\n\n<a name=\"queue\" />\n### queue(worker, concurrency)\n\nCreates a queue object with the specified concurrency. Tasks added to the\nqueue will be processed in parallel (up to the concurrency limit). If all\nworkers are in progress, the task is queued until one is available. Once\na worker has completed a task, the task's callback is called.\n\n__Arguments__\n\n* worker(task, callback) - An asynchronous function for processing a queued\n task, which must call its callback(err) argument when finished, with an \n optional error as an argument.\n* concurrency - An integer for determining how many worker functions should be\n run in parallel.\n\n__Queue objects__\n\nThe queue object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* concurrency - an integer for determining how many worker functions should be\n run in parallel. This property can be changed after a queue is created to\n alter the concurrency on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* unshift(task, [callback]) - add a new task to the front of the queue.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n```js\n// create a queue object with concurrency 2\n\nvar q = async.queue(function (task, callback) {\n console.log('hello ' + task.name);\n callback();\n}, 2);\n\n\n// assign a callback\nq.drain = function() {\n console.log('all items have been processed');\n}\n\n// add some items to the queue\n\nq.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n});\nq.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\n\n// add some items to the queue (batch-wise)\n\nq.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {\n console.log('finished processing bar');\n});\n\n// add some items to the front of the queue\n\nq.unshift({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\n```\n\n---------------------------------------\n\n<a name=\"cargo\" />\n### cargo(worker, [payload])\n\nCreates a cargo object with the specified payload. Tasks added to the\ncargo will be processed altogether (up to the payload limit). If the\nworker is in progress, the task is queued until it is available. Once\nthe worker has completed some tasks, each callback of those tasks is called.\n\n__Arguments__\n\n* worker(tasks, callback) - An asynchronous function for processing an array of\n queued tasks, which must call its callback(err) argument when finished, with \n an optional error as an argument.\n* payload - An optional integer for determining how many tasks should be\n processed per round; if omitted, the default is unlimited.\n\n__Cargo objects__\n\nThe cargo object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* payload - an integer for determining how many tasks should be\n process per round. This property can be changed after a cargo is created to\n alter the payload on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n```js\n// create a cargo object with payload 2\n\nvar cargo = async.cargo(function (tasks, callback) {\n for(var i=0; i<tasks.length; i++){\n console.log('hello ' + tasks[i].name);\n }\n callback();\n}, 2);\n\n\n// add some items\n\ncargo.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n});\ncargo.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\ncargo.push({name: 'baz'}, function (err) {\n console.log('finished processing baz');\n});\n```\n\n---------------------------------------\n\n<a name=\"auto\" />\n### auto(tasks, [callback])\n\nDetermines the best order for running functions based on their requirements.\nEach function can optionally depend on other functions being completed first,\nand each function is run as soon as its requirements are satisfied. If any of\nthe functions pass an error to their callback, that function will not complete\n(so any other functions depending on it will not run) and the main callback\nwill be called immediately with the error. Functions also receive an object\ncontaining the results of functions which have completed so far.\n\nNote, all functions are called with a results object as a second argument, \nso it is unsafe to pass functions in the tasks object which cannot handle the\nextra argument. For example, this snippet of code:\n\n```js\nasync.auto({\n readData: async.apply(fs.readFile, 'data.txt', 'utf-8');\n}, callback);\n```\n\nwill have the effect of calling readFile with the results object as the last\nargument, which will fail:\n\n```js\nfs.readFile('data.txt', 'utf-8', cb, {});\n```\n\nInstead, wrap the call to readFile in a function which does not forward the \nresults object:\n\n```js\nasync.auto({\n readData: function(cb, results){\n fs.readFile('data.txt', 'utf-8', cb);\n }\n}, callback);\n```\n\n__Arguments__\n\n* tasks - An object literal containing named functions or an array of\n requirements, with the function itself the last item in the array. The key\n used for each function or array is used when specifying requirements. The \n function receives two arguments: (1) a callback(err, result) which must be \n called when finished, passing an error (which can be null) and the result of \n the function's execution, and (2) a results object, containing the results of\n the previously executed functions.\n* callback(err, results) - An optional callback which is called when all the\n tasks have been completed. The callback will receive an error as an argument\n if any tasks pass an error to their callback. Results will always be passed\n\tbut if an error occurred, no other tasks will be performed, and the results\n\tobject will only contain partial results.\n \n\n__Example__\n\n```js\nasync.auto({\n get_data: function(callback){\n // async code to get some data\n },\n make_folder: function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n },\n write_file: ['get_data', 'make_folder', function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n callback(null, filename);\n }],\n email_link: ['write_file', function(callback, results){\n // once the file is written let's email a link to it...\n // results.write_file contains the filename returned by write_file.\n }]\n});\n```\n\nThis is a fairly trivial example, but to do this using the basic parallel and\nseries functions would look like this:\n\n```js\nasync.parallel([\n function(callback){\n // async code to get some data\n },\n function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n }\n],\nfunction(err, results){\n async.series([\n function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n },\n function(callback){\n // once the file is written let's email a link to it...\n }\n ]);\n});\n```\n\nFor a complicated series of async tasks using the auto function makes adding\nnew tasks much easier and makes the code more readable.\n\n\n---------------------------------------\n\n<a name=\"iterator\" />\n### iterator(tasks)\n\nCreates an iterator function which calls the next function in the array,\nreturning a continuation to call the next one after that. It's also possible to\n'peek' the next iterator by doing iterator.next().\n\nThis function is used internally by the async module but can be useful when\nyou want to manually control the flow of functions in series.\n\n__Arguments__\n\n* tasks - An array of functions to run.\n\n__Example__\n\n```js\nvar iterator = async.iterator([\n function(){ sys.p('one'); },\n function(){ sys.p('two'); },\n function(){ sys.p('three'); }\n]);\n\nnode> var iterator2 = iterator();\n'one'\nnode> var iterator3 = iterator2();\n'two'\nnode> iterator3();\n'three'\nnode> var nextfn = iterator2.next();\nnode> nextfn();\n'three'\n```\n\n---------------------------------------\n\n<a name=\"apply\" />\n### apply(function, arguments..)\n\nCreates a continuation function with some arguments already applied, a useful\nshorthand when combined with other control flow functions. Any arguments\npassed to the returned function are added to the arguments originally passed\nto apply.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to automatically apply when the\n continuation is called.\n\n__Example__\n\n```js\n// using apply\n\nasync.parallel([\n async.apply(fs.writeFile, 'testfile1', 'test1'),\n async.apply(fs.writeFile, 'testfile2', 'test2'),\n]);\n\n\n// the same process without using apply\n\nasync.parallel([\n function(callback){\n fs.writeFile('testfile1', 'test1', callback);\n },\n function(callback){\n fs.writeFile('testfile2', 'test2', callback);\n }\n]);\n```\n\nIt's possible to pass any number of additional arguments when calling the\ncontinuation:\n\n```js\nnode> var fn = async.apply(sys.puts, 'one');\nnode> fn('two', 'three');\none\ntwo\nthree\n```\n\n---------------------------------------\n\n<a name=\"nextTick\" />\n### nextTick(callback)\n\nCalls the callback on a later loop around the event loop. In node.js this just\ncalls process.nextTick, in the browser it falls back to setImmediate(callback)\nif available, otherwise setTimeout(callback, 0), which means other higher priority\nevents may precede the execution of the callback.\n\nThis is used internally for browser-compatibility purposes.\n\n__Arguments__\n\n* callback - The function to call on a later loop around the event loop.\n\n__Example__\n\n```js\nvar call_order = [];\nasync.nextTick(function(){\n call_order.push('two');\n // call_order now equals ['one','two']\n});\ncall_order.push('one')\n```\n\n<a name=\"times\" />\n### times(n, callback)\n\nCalls the callback n times and accumulates results in the same manner\nyou would use with async.map.\n\n__Arguments__\n\n* n - The number of times to run the function.\n* callback - The function to call n times.\n\n__Example__\n\n```js\n// Pretend this is some complicated async factory\nvar createUser = function(id, callback) {\n callback(null, {\n id: 'user' + id\n })\n}\n// generate 5 users\nasync.times(5, function(n, next){\n createUser(n, function(err, user) {\n next(err, user)\n })\n}, function(err, users) {\n // we should now have 5 users\n});\n```\n\n<a name=\"timesSeries\" />\n### timesSeries(n, callback)\n\nThe same as times only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n## Utils\n\n<a name=\"memoize\" />\n### memoize(fn, [hasher])\n\nCaches the results of an async function. When creating a hash to store function\nresults against, the callback is omitted from the hash and an optional hash\nfunction can be used.\n\nThe cache of results is exposed as the `memo` property of the function returned\nby `memoize`.\n\n__Arguments__\n\n* fn - the function you to proxy and cache results from.\n* hasher - an optional function for generating a custom hash for storing\n results, it has all the arguments applied to it apart from the callback, and\n must be synchronous.\n\n__Example__\n\n```js\nvar slow_fn = function (name, callback) {\n // do something\n callback(null, result);\n};\nvar fn = async.memoize(slow_fn);\n\n// fn can now be used as if it were slow_fn\nfn('some name', function () {\n // callback\n});\n```\n\n<a name=\"unmemoize\" />\n### unmemoize(fn)\n\nUndoes a memoized function, reverting it to the original, unmemoized\nform. Comes handy in tests.\n\n__Arguments__\n\n* fn - the memoized function\n\n<a name=\"log\" />\n### log(function, arguments)\n\nLogs the result of an async function to the console. Only works in node.js or\nin browsers that support console.log and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.log is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n```js\nvar hello = function(name, callback){\n setTimeout(function(){\n callback(null, 'hello ' + name);\n }, 1000);\n};\n```\n```js\nnode> async.log(hello, 'world');\n'hello world'\n```\n\n---------------------------------------\n\n<a name=\"dir\" />\n### dir(function, arguments)\n\nLogs the result of an async function to the console using console.dir to\ndisplay the properties of the resulting object. Only works in node.js or\nin browsers that support console.dir and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.dir is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n```js\nvar hello = function(name, callback){\n setTimeout(function(){\n callback(null, {hello: name});\n }, 1000);\n};\n```\n```js\nnode> async.dir(hello, 'world');\n{hello: 'world'}\n```\n\n---------------------------------------\n\n<a name=\"noConflict\" />\n### noConflict()\n\nChanges the value of async back to its original value, returning a reference to the\nasync object.\n",
+ "readmeFilename": "README.md",
+ "_id": "async@0.2.8",
+ "_from": "async@~0.2.7"
}
},
"readme": "# delayed-stream\n\nBuffers events from a stream until you are ready to handle them.\n\n## Installation\n\n``` bash\nnpm install delayed-stream\n```\n\n## Usage\n\nThe following example shows how to write a http echo server that delays its\nresponse by 1000 ms.\n\n``` javascript\nvar DelayedStream = require('delayed-stream');\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n var delayed = DelayedStream.create(req);\n\n setTimeout(function() {\n res.writeHead(200);\n delayed.pipe(res);\n }, 1000);\n});\n```\n\nIf you are not using `Stream#pipe`, you can also manually release the buffered\nevents by calling `delayedStream.resume()`:\n\n``` javascript\nvar delayed = DelayedStream.create(req);\n\nsetTimeout(function() {\n // Emit all buffered events and resume underlaying source\n delayed.resume();\n}, 1000);\n```\n\n## Implementation\n\nIn order to use this meta stream properly, here are a few things you should\nknow about the implementation.\n\n### Event Buffering / Proxying\n\nAll events of the `source` stream are hijacked by overwriting the `source.emit`\nmethod. Until node implements a catch-all event listener, this is the only way.\n\nHowever, delayed-stream still continues to emit all events it captures on the\n`source`, regardless of whether you have released the delayed stream yet or\nnot.\n\nUpon creation, delayed-stream captures all `source` events and stores them in\nan internal event buffer. Once `delayedStream.release()` is called, all\nbuffered events are emitted on the `delayedStream`, and the event buffer is\ncleared. After that, delayed-stream merely acts as a proxy for the underlaying\nsource.\n\n### Error handling\n\nError events on `source` are buffered / proxied just like any other events.\nHowever, `delayedStream.create` attaches a no-op `'error'` listener to the\n`source`. This way you only have to handle errors on the `delayedStream`\nobject, rather than in two places.\n\n### Buffer limits\n\ndelayed-stream provides a `maxDataSize` property that can be used to limit\nthe amount of data being buffered. In order to protect you from bad `source`\nstreams that don't react to `source.pause()`, this feature is enabled by\ndefault.\n\n## API\n\n### DelayedStream.create(source, [options])\n\nReturns a new `delayedStream`. Available options are:\n\n* `pauseStream`\n* `maxDataSize`\n\nThe description for those properties can be found below.\n\n### delayedStream.source\n\nThe `source` stream managed by this object. This is useful if you are\npassing your `delayedStream` around, and you still want to access properties\non the `source` object.\n\n### delayedStream.pauseStream = true\n\nWhether to pause the underlaying `source` when calling\n`DelayedStream.create()`. Modifying this property afterwards has no effect.\n\n### delayedStream.maxDataSize = 1024 * 1024\n\nThe amount of data to buffer before emitting an `error`.\n\nIf the underlaying source is emitting `Buffer` objects, the `maxDataSize`\nrefers to bytes.\n\nIf the underlaying source is emitting JavaScript strings, the size refers to\ncharacters.\n\nIf you know what you are doing, you can set this property to `Infinity` to\ndisable this feature. You can also modify this property during runtime.\n\n### delayedStream.maxDataSize = 1024 * 1024\n\nThe amount of data to buffer before emitting an `error`.\n\nIf the underlaying source is emitting `Buffer` objects, the `maxDataSize`\nrefers to bytes.\n\nIf the underlaying source is emitting JavaScript strings, the size refers to\ncharacters.\n\nIf you know what you are doing, you can set this property to `Infinity` to\ndisable this feature.\n\n### delayedStream.dataSize = 0\n\nThe amount of data buffered so far.\n\n### delayedStream.readable\n\nAn ECMA5 getter that returns the value of `source.readable`.\n\n### delayedStream.resume()\n\nIf the `delayedStream` has not been released so far, `delayedStream.release()`\nis called.\n\nIn either case, `source.resume()` is called.\n\n### delayedStream.pause()\n\nCalls `source.pause()`.\n\n### delayedStream.pipe(dest)\n\nCalls `delayedStream.resume()` and then proxies the arguments to `source.pipe`.\n\n### delayedStream.release()\n\nEmits and clears all events that have been buffered up so far. This does not\nresume the underlaying source, use `delayedStream.resume()` instead.\n\n## License\n\ndelayed-stream is licensed under the MIT license.\n",
"readmeFilename": "Readme.md",
- "_id": "delayed-stream@0.0.5",
- "dist": {
- "shasum": "052618e1471edc6f9affd1d89c0b0503c716f5a4"
+ "bugs": {
+ "url": "https://github.com/felixge/node-delayed-stream/issues"
},
- "_from": "delayed-stream@0.0.5",
- "_resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
+ "_id": "delayed-stream@0.0.5",
+ "_from": "delayed-stream@0.0.5"
}
},
"readme": "# combined-stream\n\nA stream that emits multiple other streams one after another.\n\n## Installation\n\n``` bash\nnpm install combined-stream\n```\n\n## Usage\n\nHere is a simple example that shows how you can use combined-stream to combine\ntwo files into one:\n\n``` javascript\nvar CombinedStream = require('combined-stream');\nvar fs = require('fs');\n\nvar combinedStream = CombinedStream.create();\ncombinedStream.append(fs.createReadStream('file1.txt'));\ncombinedStream.append(fs.createReadStream('file2.txt'));\n\ncombinedStream.pipe(fs.createWriteStream('combined.txt'));\n```\n\nWhile the example above works great, it will pause all source streams until\nthey are needed. If you don't want that to happen, you can set `pauseStreams`\nto `false`:\n\n``` javascript\nvar CombinedStream = require('combined-stream');\nvar fs = require('fs');\n\nvar combinedStream = CombinedStream.create({pauseStreams: false});\ncombinedStream.append(fs.createReadStream('file1.txt'));\ncombinedStream.append(fs.createReadStream('file2.txt'));\n\ncombinedStream.pipe(fs.createWriteStream('combined.txt'));\n```\n\nHowever, what if you don't have all the source streams yet, or you don't want\nto allocate the resources (file descriptors, memory, etc.) for them right away?\nWell, in that case you can simply provide a callback that supplies the stream\nby calling a `next()` function:\n\n``` javascript\nvar CombinedStream = require('combined-stream');\nvar fs = require('fs');\n\nvar combinedStream = CombinedStream.create();\ncombinedStream.append(function(next) {\n next(fs.createReadStream('file1.txt'));\n});\ncombinedStream.append(function(next) {\n next(fs.createReadStream('file2.txt'));\n});\n\ncombinedStream.pipe(fs.createWriteStream('combined.txt'));\n```\n\n## API\n\n### CombinedStream.create([options])\n\nReturns a new combined stream object. Available options are:\n\n* `maxDataSize`\n* `pauseStreams`\n\nThe effect of those options is described below.\n\n### combinedStream.pauseStreams = true\n\nWhether to apply back pressure to the underlaying streams. If set to `false`,\nthe underlaying streams will never be paused. If set to `true`, the\nunderlaying streams will be paused right after being appended, as well as when\n`delayedStream.pipe()` wants to throttle.\n\n### combinedStream.maxDataSize = 2 * 1024 * 1024\n\nThe maximum amount of bytes (or characters) to buffer for all source streams.\nIf this value is exceeded, `combinedStream` emits an `'error'` event.\n\n### combinedStream.dataSize = 0\n\nThe amount of bytes (or characters) currently buffered by `combinedStream`.\n\n### combinedStream.append(stream)\n\nAppends the given `stream` to the combinedStream object. If `pauseStreams` is\nset to `true, this stream will also be paused right away.\n\n`streams` can also be a function that takes one parameter called `next`. `next`\nis a function that must be invoked in order to provide the `next` stream, see\nexample above.\n\nRegardless of how the `stream` is appended, combined-stream always attaches an\n`'error'` listener to it, so you don't have to do that manually.\n\nSpecial case: `stream` can also be a String or Buffer.\n\n### combinedStream.write(data)\n\nYou should not call this, `combinedStream` takes care of piping the appended\nstreams into itself for you.\n\n### combinedStream.resume()\n\nCauses `combinedStream` to start drain the streams it manages. The function is\nidempotent, and also emits a `'resume'` event each time which usually goes to\nthe stream that is currently being drained.\n\n### combinedStream.pause();\n\nIf `combinedStream.pauseStreams` is set to `false`, this does nothing.\nOtherwise a `'pause'` event is emitted, this goes to the stream that is\ncurrently being drained, so you can use it to apply back pressure.\n\n### combinedStream.end();\n\nSets `combinedStream.writable` to false, emits an `'end'` event, and removes\nall streams from the queue.\n\n### combinedStream.destroy();\n\nSame as `combinedStream.end()`, except it emits a `'close'` event instead of\n`'end'`.\n\n## License\n\ncombined-stream is licensed under the MIT license.\n",
"readmeFilename": "Readme.md",
- "_id": "combined-stream@0.0.4",
- "dist": {
- "shasum": "818920f2e68d41ae5fac5f154dfbed98b675d34f"
+ "bugs": {
+ "url": "https://github.com/felixge/node-combined-stream/issues"
},
- "_from": "combined-stream@~0.0.4",
- "_resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.4.tgz"
+ "_id": "combined-stream@0.0.4",
+ "_from": "combined-stream@~0.0.4"
}
},
"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.0.7",
+ "version": "0.0.8",
"repository": {
"type": "git",
"url": "git://github.com/felixge/node-form-data.git"
},
"main": "./lib/form_data",
+ "scripts": {
+ "test": "node test/run.js"
+ },
"engines": {
- "node": "*"
+ "node": ">= 0.6"
},
"dependencies": {
"combined-stream": "~0.0.4",
"mime": "~1.2.2",
- "async": "~0.1.9"
+ "async": "~0.2.7"
},
"devDependencies": {
- "fake": "0.2.1",
- "far": "0.0.1",
- "formidable": "1.0.2",
- "request": "~2.9.203"
+ "fake": "~0.2.1",
+ "far": "~0.0.7",
+ "formidable": "~1.0.13",
+ "request": "~2.16.6"
},
- "readme": "# form-data\n\nA module to create readable `\"multipart/form-data\"` streams. Can be used to\nsubmit forms and file uploads to other web applications.\n\nThe API of this module is inspired by the\n[XMLHttpRequest-2 FormData Interface][xhr2-fd].\n\n[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface\n\n## Install\n\nSorry, this isn't ready for you yet.\n\n## Usage\n\nIn this example we are constructing a form with 3 fields that contain a string,\na buffer and a file stream.\n\n``` javascript\nvar FormData = require('form-data');\nvar fs = require('fs');\n\nvar form = new FormData();\nform.append('my_field', 'my value');\nform.append('my_buffer', new Buffer(10));\nform.append('my_file', fs.createReadStream('/foo/bar.jpg'));\n```\n\nAlso you can use http-response stream:\n\n``` javascript\nvar FormData = require('form-data');\nvar http = require('http');\n\nvar form = new FormData();\n\nhttp.request('http://nodejs.org/images/logo.png', function(response) {\n form.append('my_field', 'my value');\n form.append('my_buffer', new Buffer(10));\n form.append('my_logo', response);\n});\n```\n\nOr @mikeal's request stream:\n\n``` javascript\nvar FormData = require('form-data');\nvar request = require('request');\n\nvar form = new FormData();\n\nform.append('my_field', 'my value');\nform.append('my_buffer', new Buffer(10));\nform.append('my_logo', request('http://nodejs.org/images/logo.png'));\n```\n\nIn order to submit this form to a web application, you can use node's http\nclient interface:\n\n``` javascript\nvar http = require('http');\n\nvar request = http.request({\n method: 'post',\n host: 'example.org',\n path: '/upload',\n headers: form.getHeaders()\n});\n\nform.pipe(request);\n\nrequest.on('response', function(res) {\n console.log(res.statusCode);\n});\n```\n\nOr if you would prefer the `'Content-Length'` header to be set for you:\n\n``` javascript\nform.submit('example.org/upload', function(err, res) {\n console.log(res.statusCode);\n});\n```\n\nTo use custom headers and pre-known length in parts:\n\n``` javascript\nvar CRLF = '\\r\\n';\nvar form = new FormData();\n\nvar options = {\n header: CRLF + '--' + form.getBoundary() + CRLF + 'X-Custom-Header: 123' + CRLF + CRLF,\n knownLength: 1\n};\n\nform.append('my_buffer', buffer, options);\n\nform.submit('http://example.com/', function(err, res) {\n if (err) throw err;\n console.log('Done');\n});\n```\n\nFor edge cases, like POST request to URL with query string or to pass HTTP auth creadentials, object can be passed to `form.submit()` as first parameter:\n\n``` javascript\nform.submit({\n host: 'example.com',\n path: '/probably.php?extra=params',\n auth: 'username:password'\n}, function(err, res) {\n console.log(res.statusCode);\n});\n```\n\n\n[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface\n",
+ "readme": "# Form-Data [![Build Status](https://travis-ci.org/alexindigo/node-form-data.png?branch=master)](https://travis-ci.org/alexindigo/node-form-data)\n\nA module to create readable `\"multipart/form-data\"` streams. Can be used to\nsubmit forms and file uploads to other web applications.\n\nThe API of this module is inspired by the\n[XMLHttpRequest-2 FormData Interface][xhr2-fd].\n\n[xhr2-fd]: http://dev.w3.org/2006/webapi/XMLHttpRequest-2/Overview.html#the-formdata-interface\n\n## Install\n\n```\nnpm install form-data\n```\n\n## Usage\n\nIn this example we are constructing a form with 3 fields that contain a string,\na buffer and a file stream.\n\n``` javascript\nvar FormData = require('form-data');\nvar fs = require('fs');\n\nvar form = new FormData();\nform.append('my_field', 'my value');\nform.append('my_buffer', new Buffer(10));\nform.append('my_file', fs.createReadStream('/foo/bar.jpg'));\n```\n\nAlso you can use http-response stream:\n\n``` javascript\nvar FormData = require('form-data');\nvar http = require('http');\n\nvar form = new FormData();\n\nhttp.request('http://nodejs.org/images/logo.png', function(response) {\n form.append('my_field', 'my value');\n form.append('my_buffer', new Buffer(10));\n form.append('my_logo', response);\n});\n```\n\nOr @mikeal's request stream:\n\n``` javascript\nvar FormData = require('form-data');\nvar request = require('request');\n\nvar form = new FormData();\n\nform.append('my_field', 'my value');\nform.append('my_buffer', new Buffer(10));\nform.append('my_logo', request('http://nodejs.org/images/logo.png'));\n```\n\nIn order to submit this form to a web application, you can use node's http\nclient interface:\n\n``` javascript\nvar http = require('http');\n\nvar request = http.request({\n method: 'post',\n host: 'example.org',\n path: '/upload',\n headers: form.getHeaders()\n});\n\nform.pipe(request);\n\nrequest.on('response', function(res) {\n console.log(res.statusCode);\n});\n```\n\nOr if you would prefer the `'Content-Length'` header to be set for you:\n\n``` javascript\nform.submit('example.org/upload', function(err, res) {\n console.log(res.statusCode);\n});\n```\n\nTo use custom headers and pre-known length in parts:\n\n``` javascript\nvar CRLF = '\\r\\n';\nvar form = new FormData();\n\nvar options = {\n header: CRLF + '--' + form.getBoundary() + CRLF + 'X-Custom-Header: 123' + CRLF + CRLF,\n knownLength: 1\n};\n\nform.append('my_buffer', buffer, options);\n\nform.submit('http://example.com/', function(err, res) {\n if (err) throw err;\n console.log('Done');\n});\n```\n\nForm-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:\n\n``` javascript\nsomeModule.stream(function(err, stdout, stderr) {\n if (err) throw err;\n\n var form = new FormData();\n\n form.append('file', stdout, {\n filename: 'unicycle.jpg',\n contentType: 'image/jpg',\n knownLength: 19806\n });\n\n form.submit('http://example.com/', function(err, res) {\n if (err) throw err;\n console.log('Done');\n });\n});\n```\n\nFor 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:\n\n``` javascript\nform.submit({\n host: 'example.com',\n path: '/probably.php?extra=params',\n auth: 'username:password'\n}, function(err, res) {\n console.log(res.statusCode);\n});\n```\n\n## TODO\n\n- Add new streams (0.10) support and try really hard not to break it for 0.8.x.\n\n## License\n\nForm-Data is licensed under the MIT license.\n",
"readmeFilename": "Readme.md",
- "_id": "form-data@0.0.7",
- "dist": {
- "shasum": "aaa2ad518b0755c0aab584ae7e1c34f6cc8b57fd"
+ "bugs": {
+ "url": "https://github.com/felixge/node-form-data/issues"
},
- "_from": "form-data@~0.0.3",
- "_resolved": "https://registry.npmjs.org/form-data/-/form-data-0.0.7.tgz"
+ "_id": "form-data@0.0.8",
+ "_from": "form-data@0.0.8"
}
--- /dev/null
+{
+ // The tab key will cycle through the settings when first created
+ // Visit http://wbond.net/sublime_packages/sftp/settings for help
+
+ // sftp, ftp or ftps
+ "type": "sftp",
+
+ "save_before_upload": true,
+ "upload_on_save": true,
+ "sync_down_on_open": false,
+ "sync_skip_deletes": false,
+ "confirm_downloads": false,
+ "confirm_sync": true,
+ "confirm_overwrite_newer": false,
+
+ "host": "amber.exposeapp.com",
+ "user": "alex",
+ //"password": "password",
+ //"port": "22",
+
+ "remote_path": "/var/www/_playground/form-data/",
+ "ignore_regexes": [
+ "node_modules",
+ "\\.sublime-(project|workspace)", "sftp-config(-alt\\d?)?\\.json",
+ "sftp-settings\\.json", "/venv/", "\\.svn", "\\.hg", "\\.git",
+ "\\.bzr", "_darcs", "CVS", "\\.DS_Store", "Thumbs\\.db", "desktop\\.ini"
+ ],
+ //"file_permissions": "664",
+ //"dir_permissions": "775",
+
+ //"extra_list_connections": 0,
+
+ "connect_timeout": 30,
+ //"keepalive": 120,
+ //"ftp_passive_mode": true,
+ //"ssh_key_file": "~/.ssh/id_rsa",
+ //"sftp_flags": ["-F", "/path/to/ssh_config"],
+
+ //"preserve_modification_times": false,
+ //"remote_time_offset_in_hours": 0,
+ //"remote_encoding": "utf-8",
+ //"remote_locale": "C",
+}
--- /dev/null
+/*
+test custom filename and content-type:
+re: https://github.com/felixge/node-form-data/issues/29
+*/
+
+var common = require('../common');
+var assert = common.assert;
+var http = require('http');
+var fs = require('fs');
+
+var FormData = require(common.dir.lib + '/form_data');
+var IncomingForm = require('formidable').IncomingForm;
+
+var options = {
+ filename: 'test.png',
+ contentType: 'image/gif'
+};
+
+var server = http.createServer(function(req, res) {
+
+ var form = new IncomingForm({uploadDir: common.dir.tmp});
+
+ form.parse(req);
+
+ form
+ .on('file', function(name, file) {
+ assert.strictEqual(name, 'my_file');
+ assert.strictEqual(file.name, options.filename);
+ assert.strictEqual(file.type, options.contentType);
+ })
+ .on('end', function() {
+ res.writeHead(200);
+ res.end('done');
+ });
+});
+
+
+server.listen(common.port, function() {
+ var form = new FormData();
+
+ form.append('my_file', fs.createReadStream(common.dir.fixture + '/unicycle.jpg'), options);
+
+ form.submit('http://localhost:' + common.port + '/', function(err, res) {
+ if (err) {
+ throw err;
+ }
+
+ assert.strictEqual(res.statusCode, 200);
+ server.close();
+ });
+
+});
});
-
}).end();
server = http.createServer(function(req, res) {
- // formidable is broken so let's do it manual way
- //
- // var form = new IncomingForm();
- // form.uploadDir = common.dir.tmp;
- // form.parse(req);
- // form
- // .on('field', function(name, value) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(value, field.value+'');
- // })
- // .on('file', function(name, file) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(file.name, path.basename(field.value.path));
- // // mime.lookup file.NAME == 'my_file' ?
- // assert.strictEqual(file.type, mime.lookup(file.name));
- // })
- // .on('end', function() {
- // res.writeHead(200);
- // res.end('done');
- // });
-
- // temp workaround
- var data = '';
- req.setEncoding('utf8');
-
- req.on('data', function(d) {
- data += d;
- });
-
- req.on('end', function() {
-
- // check for the fields' traces
-
- // 1st field : my_field
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 2nd field : my_buffer
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 3rd field : remote_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(remoteFile)+'"') != -1 );
- // check for http://nodejs.org/images/logo.png traces
- assert.ok( data.indexOf('ImageReady') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
-
- res.writeHead(200);
- res.end('done');
-
- });
-
+ var form = new IncomingForm({uploadDir: common.dir.tmp});
+
+ form.parse(req);
+
+ form
+ .on('field', function(name, value) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(value, field.value+'');
+ })
+ .on('file', function(name, file) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ // http response doesn't have path property
+ assert.strictEqual(file.name, path.basename(field.value.path || remoteFile));
+ assert.strictEqual(file.type, mime.lookup(file.name));
+ })
+ .on('end', function() {
+ res.writeHead(200);
+ res.end('done');
+ });
});
var server = http.createServer(function(req, res) {
- // formidable is fixed on github
- // but still 7 month old in npm
- //
- // var form = new IncomingForm();
- // form.uploadDir = common.dir.tmp;
- // form.parse(req);
- // form
- // .on('field', function(name, value) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(value, field.value+'');
- // })
- // .on('file', function(name, file) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(file.name, path.basename(field.value.path));
- // assert.strictEqual(file.type, mime.lookup(file.name));
- // })
- // .on('end', function() {
- // res.writeHead(200);
- // res.end('done');
- // });
-
- // temp workaround
- var data = '';
- req.setEncoding('utf8');
-
- req.on('data', function(d) {
- data += d;
- });
-
- req.on('end', function() {
- // check for the fields' traces
-
- // 1st field : my_field
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 2nd field : my_buffer
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 3rd field : my_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
-
- // check for unicycle.jpg traces
- assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 );
-
- // 4th field : remote_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
- // check for http://nodejs.org/images/logo.png traces
- assert.ok( data.indexOf('ImageReady') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
-
- res.writeHead(200);
- res.end('done');
-
- });
-
-
+ var form = new IncomingForm({uploadDir: common.dir.tmp});
+
+ form.parse(req);
+
+ form
+ .on('field', function(name, value) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(value, field.value+'');
+ })
+ .on('file', function(name, file) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(file.name, path.basename(field.value.path));
+ assert.strictEqual(file.type, mime.lookup(file.name));
+ })
+ .on('end', function() {
+ res.writeHead(200);
+ res.end('done');
+ });
});
server.listen(common.port, function() {
var server = http.createServer(function(req, res) {
- // formidable is fixed on github
- // but still 7 month old in npm
- //
- // var form = new IncomingForm();
- // form.uploadDir = common.dir.tmp;
- // form.parse(req);
- // form
- // .on('field', function(name, value) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(value, field.value+'');
- // })
- // .on('file', function(name, file) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(file.name, path.basename(field.value.path));
- // // mime.lookup file.NAME == 'my_file' ?
- // assert.strictEqual(file.type, mime.lookup(file.name));
- // })
- // .on('end', function() {
- // res.writeHead(200);
- // res.end('done');
- // });
-
- // temp workaround
- var data = '';
- req.setEncoding('utf8');
- req.on('data', function(d) {
- data += d;
- });
- req.on('end', function() {
- // check for the fields' traces
-
- // 1st field : my_field
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 2nd field : my_buffer
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 3rd field : my_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
- // check for unicycle.jpg traces
- assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 );
-
- // 4th field : remote_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
- // check for http://nodejs.org/images/logo.png traces
- assert.ok( data.indexOf('ImageReady') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
-
- res.writeHead(200);
- res.end('done');
-
- });
-
+ var form = new IncomingForm({uploadDir: common.dir.tmp});
+
+ form.parse(req);
+
+ form
+ .on('field', function(name, value) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(value, field.value+'');
+ })
+ .on('file', function(name, file) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(file.name, path.basename(field.value.path));
+ assert.strictEqual(file.type, mime.lookup(file.name));
+ })
+ .on('end', function() {
+ res.writeHead(200);
+ res.end('done');
+ });
});
server.listen(common.port, function() {
var server = http.createServer(function(req, res) {
- // formidable is fixed on github
- // but still 7 month old in npm
- //
- // var form = new IncomingForm();
- // form.uploadDir = common.dir.tmp;
- // form.parse(req);
- // form
- // .on('field', function(name, value) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(value, field.value+'');
- // })
- // .on('file', function(name, file) {
- // var field = FIELDS.shift();
- // assert.strictEqual(name, field.name);
- // assert.strictEqual(file.name, path.basename(field.value.path));
- // // mime.lookup file.NAME == 'my_file' ?
- // assert.strictEqual(file.type, mime.lookup(file.name));
- // })
- // .on('end', function() {
- // res.writeHead(200);
- // res.end('done');
- // });
-
- // temp workaround
- var data = '';
- req.setEncoding('utf8');
- req.on('data', function(d) {
- data += d;
- });
- req.on('end', function() {
- // check for the fields' traces
-
- // 1st field : my_field
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 2nd field : my_buffer
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf(field.value) != -1 );
-
- // 3rd field : my_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
- // check for unicycle.jpg traces
- assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 );
-
- // 4th field : remote_file
- var field = FIELDS.shift();
- assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
- assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
- // check for http://nodejs.org/images/logo.png traces
- assert.ok( data.indexOf('ImageReady') != -1 );
- assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
-
- res.writeHead(200);
- res.end('done');
-
- });
-
+ var form = new IncomingForm({uploadDir: common.dir.tmp});
+
+ form.parse(req);
+
+ form
+ .on('field', function(name, value) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(value, field.value+'');
+ })
+ .on('file', function(name, file) {
+ var field = FIELDS.shift();
+ assert.strictEqual(name, field.name);
+ assert.strictEqual(file.name, path.basename(field.value.path));
+ assert.strictEqual(file.type, mime.lookup(file.name));
+ })
+ .on('end', function() {
+ res.writeHead(200);
+ res.end('done');
+ });
});
server.listen(common.port, function() {
language: node_js
node_js:
- - 0.8
+ - 0.10
-test:\r
- @./node_modules/.bin/lab\r
-test-cov: \r
- @./node_modules/.bin/lab -r threshold -t 100\r
-test-cov-html:\r
- @./node_modules/.bin/lab -r html -o coverage.html\r
-complexity:\r
- @./node_modules/.bin/cr -o complexity.md -f markdown lib\r
-\r
-.PHONY: test test-cov test-cov-html complexity\r
-\r
+test:
+ @./node_modules/.bin/lab
+test-cov:
+ @./node_modules/.bin/lab -r threshold -t 100
+test-cov-html:
+ @./node_modules/.bin/lab -r html -o coverage.html
+complexity:
+ @./node_modules/.bin/cr -o complexity.md -f markdown lib
+
+.PHONY: test test-cov test-cov-html complexity
+
<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\r
HTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](https://github.com/hueniverse/oz).\r
\r
-Current version: **0.10.1**\r
+Current version: **0.13**\r
\r
[![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk)\r
\r
- [Protocol Example](#protocol-example)\r
- [Payload Validation](#payload-validation)\r
- [Response Payload Validation](#response-payload-validation)\r
+ - [Browser Support and Considerations](#browser-support-and-considerations)\r
<p></p>\r
- [**Single URI Authorization**](#single-uri-authorization)\r
- [Usage Example](#bewit-usage-example)\r
\r
Using a timestamp requires the client's clock to be in sync with the server's clock. **Hawk** requires both the client\r
clock and the server clock to use NTP to ensure synchronization. However, given the limitations of some client types\r
-(e.g. browsers) to deploy NTP, the server provides the client with its current time in response to a bad timestamp.\r
+(e.g. browsers) to deploy NTP, the server provides the client with its current time (in seconds precision) in response\r
+to a bad timestamp.\r
\r
There is no expectation that the client will adjust its system clock to match the server (in fact, this would be a\r
potential attack vector). Instead, the client only uses the server's time to calculate an offset used only\r
\r
// Generate Server-Authorization response header\r
\r
- var header = Hawk.server.header(artifacts, { payload: payload, contentType: headers['Content-Type'] });\r
+ var header = Hawk.server.header(credentials, artifacts, { payload: payload, contentType: headers['Content-Type'] });\r
headers['Server-Authorization'] = header;\r
\r
// Send the response back\r
\r
// Authenticate the server's response\r
\r
- var isValid = Hawk.client.authenticate(response, header.artifacts, { payload: body });\r
+ var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });\r
\r
// Output results\r
\r
```\r
\r
\r
+## Browser Support and Considerations\r
+\r
+A browser script is provided for including using a `<script>` tag in [lib/browser.js](/lib/browser.js).\r
+\r
+**Hawk** relies on the _Server-Authorization_ and _WWW-Authenticate_ headers in its response to communicate with the client.\r
+Therefore, in case of CORS requests, it is important to consider sending _Access-Control-Expose-Headers_ with the value\r
+_"WWW-Authenticate, Server-Authorization"_ on each response from your server. As explained in the\r
+[specifications](http://www.w3.org/TR/cors/#access-control-expose-headers-response-header), it will indicate that these headers\r
+can safely be accessed by the client (using getResponseHeader() on the XmlHttpRequest object). Otherwise you will be met with a\r
+["simple response header"](http://www.w3.org/TR/cors/#simple-response-header) which excludes these fields and would prevent the\r
+Hawk client from authenticating the requests.You can read more about the why and how in this\r
+[article](http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server)\r
+\r
+\r
# Single URI Authorization\r
\r
There are cases in which limited and short-term access to a protected resource is granted to a third party which does not\r
at work to produce more garbage.\r
\r
**Hawk** provides a simple HTTP authentication scheme for making client-server requests. It does not address the OAuth use case\r
-of delegating access to a third party. If you are looking for an OAuth alternative, check out [Oz](/hueniverse/oz).\r
+of delegating access to a third party. If you are looking for an OAuth alternative, check out [Oz](https://github.com/hueniverse/oz).\r
\r
\r
# Acknowledgements\r
// Declare internals
-var internals = {\r
- credentials: {\r
- dh37fgj492je: {\r
+var internals = {
+ credentials: {
+ dh37fgj492je: {
id: 'dh37fgj492je', // Required by Hawk.client.header
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
- user: 'Steve'\r
- }\r
- }\r
+ user: 'Steve'
+ }
+ }
};
// Credentials lookup function
-var credentialsFunc = function (id, callback) {\r
+var credentialsFunc = function (id, callback) {
- return callback(null, internals.credentials[id]);\r
+ return callback(null, internals.credentials[id]);
};
// Create HTTP server
-var handler = function (req, res) {\r
+var handler = function (req, res) {
- Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');
- var headers = {\r
- 'Content-Type': 'text/plain',\r
- 'Server-Authorization': Hawk.server.header(artifacts, { payload: payload, contentType: 'text/plain' })\r
+ 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);\r
- });\r
+ 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) {\r
+Request('http://127.0.0.1:8000/resource/1?b=1&a=2', function (error, response, body) {
- console.log(response.statusCode + ': ' + body);\r
+ console.log(response.statusCode + ': ' + body);
});
// Send authenticated request
-var header = Hawk.client.header('http://127.0.0.1:8000/resource/1?b=1&a=2', 'GET', { credentials: internals.credentials.dh37fgj492je, ext: 'and welcome!' });
-var options = {\r
- uri: 'http://127.0.0.1:8000/resource/1?b=1&a=2',
- method: 'GET',
- headers: {\r
- authorization: header.field\r
- }\r
-};
+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) {\r
+ Request(options, function (error, response, body) {
- var isValid = Hawk.client.authenticate(response, header.artifacts, { payload: body });
- console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
- process.exit(0);\r
+ var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
+ console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
+ process.exit(0);
+ });
});
-
--- /dev/null
+/*\r
+ HTTP Hawk Authentication Scheme\r
+ Copyright (c) 2012-2013, Eran Hammer <eran@hueniverse.com>\r
+ MIT Licensed\r
+*/\r
+\r
+\r
+// Declare namespace\r
+\r
+var hawk = {};\r
+\r
+\r
+// Export if used as a module\r
+\r
+if (typeof module !== "undefined" && module.exports) {\r
+ module.exports = hawk;\r
+}\r
+\r
+// Generate an Authorization header for a given request\r
+\r
+/*\r
+ uri: 'http://example.com/resource?a=b'\r
+ method: HTTP verb (e.g. 'GET', 'POST')\r
+ options: {\r
+\r
+ // Required\r
+\r
+ credentials: {\r
+ id: 'dh37fgj492je',\r
+ key: 'aoijedoaijsdlaksjdl',\r
+ algorithm: 'sha256' // 'sha1', 'sha256'\r
+ },\r
+\r
+ // Optional\r
+\r
+ ext: 'application-specific', // Application specific data sent via the ext attribute\r
+ timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds\r
+ nonce: '2334f34f', // A pre-generated nonce\r
+ localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)\r
+ payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)\r
+ contentType: 'application/json', // Payload content-type (ignored if hash provided)\r
+ hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash\r
+ app: '24s23423f34dx', // Oz application id\r
+ dlg: '234sz34tww3sd' // Oz delegated-by application id\r
+ }\r
+*/\r
+\r
+hawk.client = {\r
+\r
+ header: function (uri, method, options) {\r
+\r
+ var result = {\r
+ field: '',\r
+ artifacts: {}\r
+ };\r
+\r
+ // Validate inputs\r
+\r
+ if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||\r
+ !method || typeof method !== 'string' ||\r
+ !options || typeof options !== 'object') {\r
+\r
+ return result;\r
+ }\r
+\r
+ // Application time\r
+\r
+ var timestamp = options.timestamp || Math.floor((hawk.utils.now() + (options.localtimeOffsetMsec || 0)) / 1000)\r
+\r
+ // Validate credentials\r
+\r
+ var credentials = options.credentials;\r
+ if (!credentials ||\r
+ !credentials.id ||\r
+ !credentials.key ||\r
+ !credentials.algorithm) {\r
+\r
+ // Invalid credential object\r
+ return result;\r
+ }\r
+\r
+ if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
+ return result;\r
+ }\r
+\r
+ // Parse URI\r
+\r
+ if (typeof uri === 'string') {\r
+ uri = hawk.utils.parseUri(uri);\r
+ }\r
+\r
+ // Calculate signature\r
+\r
+ var artifacts = {\r
+ ts: timestamp,\r
+ nonce: options.nonce || hawk.utils.randomString(6),\r
+ method: method,\r
+ resource: uri.relative,\r
+ host: uri.hostname,\r
+ port: uri.port,\r
+ hash: options.hash,\r
+ ext: options.ext,\r
+ app: options.app,\r
+ dlg: options.dlg\r
+ };\r
+\r
+ result.artifacts = artifacts;\r
+\r
+ // Calculate payload hash\r
+\r
+ if (!artifacts.hash &&\r
+ options.hasOwnProperty('payload')) {\r
+\r
+ artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);\r
+ }\r
+\r
+ var mac = hawk.crypto.calculateMac('header', credentials, artifacts);\r
+\r
+ // Construct header\r
+\r
+ var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed\r
+ var header = 'Hawk id="' + credentials.id +\r
+ '", ts="' + artifacts.ts +\r
+ '", nonce="' + artifacts.nonce +\r
+ (artifacts.hash ? '", hash="' + artifacts.hash : '') +\r
+ (hasExt ? '", ext="' + hawk.utils.escapeHeaderAttribute(artifacts.ext) : '') +\r
+ '", mac="' + mac + '"';\r
+\r
+ if (artifacts.app) {\r
+ header += ', app="' + artifacts.app +\r
+ (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';\r
+ }\r
+\r
+ result.field = header;\r
+\r
+ return result;\r
+ },\r
+\r
+\r
+ // Validate server response\r
+\r
+ /*\r
+ request: object created via 'new XMLHttpRequest()' after response received\r
+ artifacts: object recieved from header().artifacts\r
+ options: {\r
+ payload: optional payload received\r
+ required: specifies if a Server-Authorization header is required. Defaults to 'false'\r
+ }\r
+ */\r
+\r
+ authenticate: function (request, credentials, artifacts, options) {\r
+\r
+ options = options || {};\r
+\r
+ if (request.getResponseHeader('www-authenticate')) {\r
+\r
+ // Parse HTTP WWW-Authenticate header\r
+\r
+ var attributes = hawk.utils.parseAuthorizationHeader(request.getResponseHeader('www-authenticate'), ['ts', 'tsm', 'error']);\r
+ if (!attributes) {\r
+ return false;\r
+ }\r
+\r
+ if (attributes.ts) {\r
+ var tsm = hawk.crypto.calculateTsMac(attributes.ts, credentials);\r
+ if (tsm !== attributes.tsm) {\r
+ return false;\r
+ }\r
+\r
+ hawk.utils.setNtpOffset(attributes.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision\r
+ }\r
+ }\r
+\r
+ // Parse HTTP Server-Authorization header\r
+\r
+ if (!request.getResponseHeader('server-authorization') &&\r
+ !options.required) {\r
+\r
+ return true;\r
+ }\r
+\r
+ var attributes = hawk.utils.parseAuthorizationHeader(request.getResponseHeader('server-authorization'), ['mac', 'ext', 'hash']);\r
+ if (!attributes) {\r
+ return false;\r
+ }\r
+\r
+ var modArtifacts = {\r
+ ts: artifacts.ts,\r
+ nonce: artifacts.nonce,\r
+ method: artifacts.method,\r
+ resource: artifacts.resource,\r
+ host: artifacts.host,\r
+ port: artifacts.port,\r
+ hash: attributes.hash,\r
+ ext: attributes.ext,\r
+ app: artifacts.app,\r
+ dlg: artifacts.dlg\r
+ };\r
+\r
+ var mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);\r
+ if (mac !== attributes.mac) {\r
+ return false;\r
+ }\r
+\r
+ if (!options.hasOwnProperty('payload')) {\r
+ return true;\r
+ }\r
+\r
+ if (!attributes.hash) {\r
+ return false;\r
+ }\r
+\r
+ var calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.getResponseHeader('content-type'));\r
+ return (calculatedHash === attributes.hash);\r
+ }\r
+};\r
+\r
+\r
+hawk.crypto = {\r
+\r
+ headerVersion: '1',\r
+\r
+ algorithms: ['sha1', 'sha256'],\r
+\r
+ calculateMac: function (type, credentials, options) {\r
+\r
+ var normalized = hawk.crypto.generateNormalizedString(type, options);\r
+\r
+ var hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);\r
+ return hmac.toString(CryptoJS.enc.Base64);\r
+ },\r
+\r
+ generateNormalizedString: function (type, options) {\r
+\r
+ var normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' +\r
+ options.ts + '\n' +\r
+ options.nonce + '\n' +\r
+ options.method.toUpperCase() + '\n' +\r
+ options.resource + '\n' +\r
+ options.host.toLowerCase() + '\n' +\r
+ options.port + '\n' +\r
+ (options.hash || '') + '\n';\r
+\r
+ if (options.ext) {\r
+ normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');\r
+ }\r
+\r
+ normalized += '\n';\r
+\r
+ if (options.app) {\r
+ normalized += options.app + '\n' +\r
+ (options.dlg || '') + '\n';\r
+ }\r
+\r
+ return normalized;\r
+ },\r
+\r
+ calculatePayloadHash: function (payload, algorithm, contentType) {\r
+\r
+ var hash = CryptoJS.algo[algorithm.toUpperCase()].create();\r
+ hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');\r
+ hash.update(hawk.utils.parseContentType(contentType) + '\n');\r
+ hash.update(payload || '');\r
+ hash.update('\n');\r
+ return hash.finalize().toString(CryptoJS.enc.Base64);\r
+ },\r
+\r
+ calculateTsMac: function (ts, credentials) {\r
+\r
+ var hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);\r
+ return hash.toString(CryptoJS.enc.Base64);\r
+ }\r
+};\r
+\r
+\r
+hawk.utils = {\r
+\r
+ storage: { // localStorage compatible interface\r
+ _cache: {},\r
+ setItem: function (key, value) {\r
+\r
+ hawk.utils.storage._cache[key] = value;\r
+ },\r
+ getItem: function (key) {\r
+\r
+ return hawk.utils.storage._cache[key];\r
+ }\r
+ },\r
+\r
+ setStorage: function (storage) {\r
+\r
+ var ntpOffset = hawk.utils.getNtpOffset() || 0;\r
+ hawk.utils.storage = storage;\r
+ hawk.utils.setNtpOffset(ntpOffset);\r
+ },\r
+\r
+ setNtpOffset: function (offset) {\r
+\r
+ hawk.utils.storage.setItem('hawk_ntp_offset', offset);\r
+ },\r
+\r
+ getNtpOffset: function () {\r
+\r
+ return parseInt(hawk.utils.storage.getItem('hawk_ntp_offset') || '0', 10);\r
+ },\r
+\r
+ now: function () {\r
+\r
+ return Date.now() + hawk.utils.getNtpOffset();\r
+ },\r
+\r
+ escapeHeaderAttribute: function (attribute) {\r
+\r
+ return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');\r
+ },\r
+\r
+ parseContentType: function (header) {\r
+\r
+ if (!header) {\r
+ return '';\r
+ }\r
+\r
+ return header.split(';')[0].trim().toLowerCase();\r
+ },\r
+\r
+ parseAuthorizationHeader: function (header, keys) {\r
+\r
+ if (!header) {\r
+ return null;\r
+ }\r
+\r
+ var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]\r
+ if (!headerParts) {\r
+ return null;\r
+ }\r
+\r
+ var scheme = headerParts[1];\r
+ if (scheme.toLowerCase() !== 'hawk') {\r
+ return null;\r
+ }\r
+\r
+ var attributesString = headerParts[2];\r
+ if (!attributesString) {\r
+ return null;\r
+ }\r
+\r
+ var attributes = {};\r
+ var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {\r
+\r
+ // Check valid attribute names\r
+\r
+ if (keys.indexOf($1) === -1) {\r
+ return;\r
+ }\r
+\r
+ // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9\r
+\r
+ if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {\r
+ return;\r
+ }\r
+\r
+ // Check for duplicates\r
+\r
+ if (attributes.hasOwnProperty($1)) {\r
+ return;\r
+ }\r
+\r
+ attributes[$1] = $2;\r
+ return '';\r
+ });\r
+\r
+ if (verify !== '') {\r
+ return null;\r
+ }\r
+\r
+ return attributes;\r
+ },\r
+\r
+ randomString: function (size) {\r
+\r
+ var randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\r
+ var len = randomSource.length;\r
+\r
+ var result = [];\r
+ for (var i = 0; i < size; ++i) {\r
+ result[i] = randomSource[Math.floor(Math.random() * len)];\r
+ }\r
+\r
+ return result.join('');\r
+ },\r
+\r
+ parseUri: function (input) {\r
+\r
+ // Based on: parseURI 1.2.2\r
+ // http://blog.stevenlevithan.com/archives/parseuri\r
+ // (c) Steven Levithan <stevenlevithan.com>\r
+ // MIT License\r
+\r
+ var keys = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'hostname', 'port', 'resource', 'relative', 'pathname', 'directory', 'file', 'query', 'fragment'];\r
+\r
+ var uriRegex = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?)(?:#(.*))?)/;\r
+ var uriByNumber = uriRegex.exec(input);\r
+ var uri = {};\r
+\r
+ var i = 15;\r
+ while (i--) {\r
+ uri[keys[i]] = uriByNumber[i] || '';\r
+ }\r
+\r
+ if (uri.port === null ||\r
+ uri.port === '') {\r
+\r
+ uri.port = (uri.protocol.toLowerCase() === 'http' ? '80' : (uri.protocol.toLowerCase() === 'https' ? '443' : ''));\r
+ }\r
+\r
+ return uri;\r
+ }\r
+};\r
+\r
+\r
+// Based on: Crypto-JS v3.1.2\r
+// Copyright (c) 2009-2013, Jeff Mott. All rights reserved.\r
+// http://code.google.com/p/crypto-js/\r
+// http://code.google.com/p/crypto-js/wiki/License\r
+\r
+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);\r
+(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) })();\r
+(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);\r
+(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))}})})();\r
+(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+/="}})();\r
-// 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') {
-
- return result;
- }
-
- // Application time
-
- var timestamp = options.timestamp || Math.floor((Utils.now() + (options.localtimeOffsetMsec || 0)) / 1000)
-
- // Validate credentials
-
- var credentials = options.credentials;
- if (!credentials ||
- !credentials.id ||
- !credentials.key ||
- !credentials.algorithm) {
-
- // Invalid credential object
- return result;
- }
-
- if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
- return result;
- }
-
- // Parse URI
-
- if (typeof uri === 'string') {
- uri = Url.parse(uri);
- }
-
- // Calculate signature
-
- var artifacts = {
- credentials: credentials,
- 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.hasOwnProperty('payload')) {
-
- artifacts.hash = Crypto.calculateHash(options.payload, credentials.algorithm, options.contentType);
- }
-
- var mac = Crypto.calculateMac('header', 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="' + Utils.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 recieved from header().artifacts
- options: {
- payload: optional payload received
- required: specifies if a Server-Authorization header is required. Defaults to 'false'
- }
-*/
-
-exports.authenticate = function (res, 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;
- }
-
- if (attributes.ts) {
- var tsm = Crypto.calculateTsMac(attributes.ts, artifacts.credentials);
- if (!Cryptiles.fixedTimeComparison(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', artifacts);
- if (!Cryptiles.fixedTimeComparison(mac, attributes.mac)) {
- return false;
- }
-
- if (!options.hasOwnProperty('payload')) {
- return true;
- }
-
+// Load modules\r
+\r
+var Url = require('url');\r
+var Hoek = require('hoek');\r
+var Cryptiles = require('cryptiles');\r
+var Crypto = require('./crypto');\r
+var Utils = require('./utils');\r
+\r
+\r
+// Declare internals\r
+\r
+var internals = {};\r
+\r
+\r
+// Generate an Authorization header for a given request\r
+\r
+/*\r
+ uri: 'http://example.com/resource?a=b' or object from Url.parse()\r
+ method: HTTP verb (e.g. 'GET', 'POST')\r
+ options: {\r
+\r
+ // Required\r
+\r
+ credentials: {\r
+ id: 'dh37fgj492je',\r
+ key: 'aoijedoaijsdlaksjdl',\r
+ algorithm: 'sha256' // 'sha1', 'sha256'\r
+ },\r
+\r
+ // Optional\r
+\r
+ ext: 'application-specific', // Application specific data sent via the ext attribute\r
+ timestamp: Date.now(), // A pre-calculated timestamp\r
+ nonce: '2334f34f', // A pre-generated nonce\r
+ localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)\r
+ payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)\r
+ contentType: 'application/json', // Payload content-type (ignored if hash provided)\r
+ hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash\r
+ app: '24s23423f34dx', // Oz application id\r
+ dlg: '234sz34tww3sd' // Oz delegated-by application id\r
+ }\r
+*/\r
+\r
+exports.header = function (uri, method, options) {\r
+\r
+ var result = {\r
+ field: '',\r
+ artifacts: {}\r
+ };\r
+\r
+ // Validate inputs\r
+\r
+ if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||\r
+ !method || typeof method !== 'string' ||\r
+ !options || typeof options !== 'object') {\r
+\r
+ return result;\r
+ }\r
+\r
+ // Application time\r
+\r
+ var timestamp = options.timestamp || Math.floor((Utils.now() + (options.localtimeOffsetMsec || 0)) / 1000)\r
+\r
+ // Validate credentials\r
+\r
+ var credentials = options.credentials;\r
+ if (!credentials ||\r
+ !credentials.id ||\r
+ !credentials.key ||\r
+ !credentials.algorithm) {\r
+\r
+ // Invalid credential object\r
+ return result;\r
+ }\r
+\r
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
+ return result;\r
+ }\r
+\r
+ // Parse URI\r
+\r
+ if (typeof uri === 'string') {\r
+ uri = Url.parse(uri);\r
+ }\r
+\r
+ // Calculate signature\r
+\r
+ var artifacts = {\r
+ ts: timestamp,\r
+ nonce: options.nonce || Cryptiles.randomString(6),\r
+ method: method,\r
+ resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'\r
+ host: uri.hostname,\r
+ port: uri.port || (uri.protocol === 'http:' ? 80 : 443),\r
+ hash: options.hash,\r
+ ext: options.ext,\r
+ app: options.app,\r
+ dlg: options.dlg\r
+ };\r
+\r
+ result.artifacts = artifacts;\r
+\r
+ // Calculate payload hash\r
+\r
+ if (!artifacts.hash &&\r
+ options.hasOwnProperty('payload')) {\r
+\r
+ artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);\r
+ }\r
+\r
+ var mac = Crypto.calculateMac('header', credentials, artifacts);\r
+\r
+ // Construct header\r
+\r
+ var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed\r
+ var header = 'Hawk id="' + credentials.id +\r
+ '", ts="' + artifacts.ts +\r
+ '", nonce="' + artifacts.nonce +\r
+ (artifacts.hash ? '", hash="' + artifacts.hash : '') +\r
+ (hasExt ? '", ext="' + Utils.escapeHeaderAttribute(artifacts.ext) : '') +\r
+ '", mac="' + mac + '"';\r
+\r
+ if (artifacts.app) {\r
+ header += ', app="' + artifacts.app +\r
+ (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';\r
+ }\r
+\r
+ result.field = header;\r
+\r
+ return result;\r
+};\r
+\r
+\r
+// Validate server response\r
+\r
+/*\r
+ res: node's response object\r
+ artifacts: object recieved from header().artifacts\r
+ options: {\r
+ payload: optional payload received\r
+ required: specifies if a Server-Authorization header is required. Defaults to 'false'\r
+ }\r
+*/\r
+\r
+exports.authenticate = function (res, credentials, artifacts, options) {\r
+\r
+ artifacts = Hoek.clone(artifacts);\r
+ options = options || {};\r
+\r
+ if (res.headers['www-authenticate']) {\r
+\r
+ // Parse HTTP WWW-Authenticate header\r
+\r
+ var attributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);\r
+ if (attributes instanceof Error) {\r
+ return false;\r
+ }\r
+\r
+ if (attributes.ts) {\r
+ var tsm = Crypto.calculateTsMac(attributes.ts, credentials);\r
+ if (tsm !== attributes.tsm) {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Parse HTTP Server-Authorization header\r
+\r
+ if (!res.headers['server-authorization'] &&\r
+ !options.required) {\r
+\r
+ return true;\r
+ }\r
+\r
+ var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);\r
+ if (attributes instanceof Error) {\r
+ return false;\r
+ }\r
+\r
+ artifacts.ext = attributes.ext;\r
+ artifacts.hash = attributes.hash;\r
+\r
+ var mac = Crypto.calculateMac('response', credentials, artifacts);\r
+ if (mac !== attributes.mac) {\r
+ return false;\r
+ }\r
+\r
+ if (!options.hasOwnProperty('payload')) {\r
+ return true;\r
+ }\r
+\r
if (!attributes.hash) {\r
return false;\r
- }
-
- var calculatedHash = Crypto.calculateHash(options.payload, artifacts.credentials.algorithm, res.headers['content-type']);
- return Cryptiles.fixedTimeComparison(calculatedHash, attributes.hash);
-};
-
+ }\r
+\r
+ var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);\r
+ return (calculatedHash === attributes.hash);\r
+};\r
+\r
+\r
+// Generate a bewit value for a given URI\r
+\r
+/*\r
+ * credentials is an object with the following keys: 'id, 'key', 'algorithm'.\r
+ * options is an object with the following optional keys: 'ext', 'localtimeOffsetMsec'\r
+ */\r
+/*\r
+ uri: 'http://example.com/resource?a=b' or object from Url.parse()\r
+ options: {\r
+\r
+ // Required\r
+\r
+ credentials: {\r
+ id: 'dh37fgj492je',\r
+ key: 'aoijedoaijsdlaksjdl',\r
+ algorithm: 'sha256' // 'sha1', 'sha256'\r
+ },\r
+ ttlSec: 60 * 60, // TTL in seconds\r
+\r
+ // Optional\r
+\r
+ ext: 'application-specific', // Application specific data sent via the ext attribute\r
+ localtimeOffsetMsec: 400 // Time offset to sync with server time\r
+ };\r
+*/\r
+\r
+exports.getBewit = function (uri, options) {\r
+\r
+ // Validate inputs\r
+\r
+ if (!uri ||\r
+ (typeof uri !== 'string' && typeof uri !== 'object') ||\r
+ !options ||\r
+ typeof options !== 'object' ||\r
+ !options.ttlSec) {\r
+\r
+ return '';\r
+ }\r
+\r
+ options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext); // Zero is valid value\r
+\r
+ // Application time\r
+\r
+ var now = Utils.now() + (options.localtimeOffsetMsec || 0);\r
+\r
+ // Validate credentials\r
+\r
+ var credentials = options.credentials;\r
+ if (!credentials ||\r
+ !credentials.id ||\r
+ !credentials.key ||\r
+ !credentials.algorithm) {\r
+\r
+ return '';\r
+ }\r
+\r
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
+ return '';\r
+ }\r
+\r
+ // Parse URI\r
+\r
+ if (typeof uri === 'string') {\r
+ uri = Url.parse(uri);\r
+ }\r
+\r
+ // Calculate signature\r
+\r
+ var exp = Math.floor(now / 1000) + options.ttlSec;\r
+ var mac = Crypto.calculateMac('bewit', credentials, {\r
+ ts: exp,\r
+ nonce: '',\r
+ method: 'GET',\r
+ resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'\r
+ host: uri.hostname,\r
+ port: uri.port || (uri.protocol === 'http:' ? 80 : 443),\r
+ ext: options.ext\r
+ });\r
+\r
+ // Construct bewit: id\exp\mac\ext\r
+\r
+ var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;\r
+ return Utils.base64urlEncode(bewit);\r
+};\r
+\r
-// 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'
- options: {
- credentials: {
- key: 'aoijedoaijsdlaksjdl',
- algorithm: 'sha256' // 'sha1', 'sha256'
- },
- 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, options) {
-
- var normalized = exports.generateNormalizedString(type, options);
-
- var hmac = Crypto.createHmac(options.credentials.algorithm, options.credentials.key).update(normalized);
- var digest = hmac.digest('base64');
- return digest;
-};
-
-
-exports.generateNormalizedString = function (type, options) {
-
- var normalized = 'hawk.' + exports.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';
-
+// Load modules\r
+\r
+var Crypto = require('crypto');\r
+var Url = require('url');\r
+var Utils = require('./utils');\r
+\r
+\r
+// Declare internals\r
+\r
+var internals = {};\r
+\r
+\r
+// MAC normalization format version\r
+\r
+exports.headerVersion = '1'; // Prevent comparison of mac values generated with different normalized string formats\r
+\r
+\r
+// Supported HMAC algorithms\r
+\r
+exports.algorithms = ['sha1', 'sha256'];\r
+\r
+\r
+// Calculate the request MAC\r
+\r
+/*\r
+ type: 'header', // 'header', 'bewit', 'response'\r
+ credentials: {\r
+ key: 'aoijedoaijsdlaksjdl',\r
+ algorithm: 'sha256' // 'sha1', 'sha256'\r
+ },\r
+ options: {\r
+ method: 'GET',\r
+ resource: '/resource?a=1&b=2',\r
+ host: 'example.com',\r
+ port: 8080,\r
+ ts: 1357718381034,\r
+ nonce: 'd3d345f',\r
+ hash: 'U4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=',\r
+ ext: 'app-specific-data',\r
+ app: 'hf48hd83qwkj', // Application id (Oz)\r
+ dlg: 'd8djwekds9cj' // Delegated by application id (Oz), requires options.app\r
+ }\r
+*/\r
+\r
+exports.calculateMac = function (type, credentials, options) {\r
+\r
+ var normalized = exports.generateNormalizedString(type, options);\r
+\r
+ var hmac = Crypto.createHmac(credentials.algorithm, credentials.key).update(normalized);\r
+ var digest = hmac.digest('base64');\r
+ return digest;\r
+};\r
+\r
+\r
+exports.generateNormalizedString = function (type, options) {\r
+\r
+ var normalized = 'hawk.' + exports.headerVersion + '.' + type + '\n' +\r
+ options.ts + '\n' +\r
+ options.nonce + '\n' +\r
+ options.method.toUpperCase() + '\n' +\r
+ options.resource + '\n' +\r
+ options.host.toLowerCase() + '\n' +\r
+ options.port + '\n' +\r
+ (options.hash || '') + '\n';\r
+\r
if (options.ext) {\r
normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');\r
- }
-
- normalized += '\n';
-
+ }\r
+\r
+ normalized += '\n';\r
+\r
if (options.app) {\r
normalized += options.app + '\n' +\r
(options.dlg || '') + '\n';\r
- }
-
- return normalized;
-};
-
-
-exports.calculateHash = function (payload, algorithm, contentType) {
-
- var hash = Crypto.createHash(algorithm);
- hash.update('hawk.' + exports.headerVersion + '.payload\n');
- hash.update(Utils.parseContentType(contentType) + '\n');
- hash.update(payload || '');
- hash.update('\n');
- return hash.digest('base64');
-};
-
-
-exports.calculateTsMac = function (ts, credentials) {\r
-
- var hash = Crypto.createHash(credentials.algorithm);
- hash.update('hawk.' + exports.headerVersion + '.ts\n' + ts + '\n');
+ }\r
+\r
+ return normalized;\r
+};\r
+\r
+\r
+exports.calculatePayloadHash = function (payload, algorithm, contentType) {\r
+\r
+ var hash = exports.initializePayloadHash(algorithm, contentType);\r
+ hash.update(payload || '');\r
+ return exports.finalizePayloadHash(hash);\r
+};\r
+\r
+\r
+exports.initializePayloadHash = function (algorithm, contentType) {\r
+\r
+ var hash = Crypto.createHash(algorithm);\r
+ hash.update('hawk.' + exports.headerVersion + '.payload\n');\r
+ hash.update(Utils.parseContentType(contentType) + '\n');\r
+ return hash;\r
+};\r
+\r
+\r
+exports.finalizePayloadHash = function (hash) {\r
+\r
+ hash.update('\n');\r
return hash.digest('base64');\r
-};
-
+};\r
+\r
+\r
+exports.calculateTsMac = function (ts, credentials) {\r
+\r
+ var hmac = Crypto.createHmac(credentials.algorithm, credentials.key);\r
+ hmac.update('hawk.' + exports.headerVersion + '.ts\n' + ts + '\n');\r
+ return hmac.digest('base64');\r
+};\r
+\r
-// Export sub-modules
-
-exports.error = exports.Error = require('boom');
-exports.sntp = require('sntp');
-exports.server = require('./server');
-exports.client = require('./client');
-exports.uri = require('./uri');
-exports.crypto = require('./crypto');
-exports.utils = require('./utils');
-
-
+// Export sub-modules\r
+\r
+exports.error = exports.Error = require('boom');\r
+exports.sntp = require('sntp');\r
+exports.server = require('./server');\r
+exports.client = require('./client');\r
+exports.crypto = require('./crypto');\r
+exports.utils = require('./utils');\r
+\r
+exports.uri = {\r
+ authenticate: exports.server.authenticateBewit,\r
+ getBewit: exports.client.getBewit\r
+};\r
+\r
+\r
-// 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.
- }
-
- callback: function (err, credentials, artifacts) { }
- */
-
-exports.authenticate = function (req, credentialsFunc, options, callback) {
-
- // Default options
-
- options.nonceFunc = options.nonceFunc || function (nonce, ts, callback) { return callback(); }; // No validation
- options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds
-
- // Application time
-
- var now = Utils.now() + (options.localtimeOffsetMsec || 0); // Measure now before any other processing
-
- // Convert node Http request object to a request configuration object
-
- var request = Utils.parseRequest(req, options);
+// Load modules\r
+\r
+var Boom = require('boom');\r
+var Hoek = require('hoek');\r
+var Cryptiles = require('cryptiles');\r
+var Crypto = require('./crypto');\r
+var Utils = require('./utils');\r
+\r
+\r
+// Declare internals\r
+\r
+var internals = {};\r
+\r
+\r
+// Hawk authentication\r
+\r
+/*\r
+ req: node's HTTP request object or an object as follows:\r
+ \r
+ var request = {\r
+ method: 'GET',\r
+ url: '/resource/4?a=1&b=2',\r
+ host: 'example.com',\r
+ port: 8080,\r
+ authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="'\r
+ };\r
+ \r
+ credentialsFunc: required function to lookup the set of Hawk credentials based on the provided credentials id.\r
+ The credentials include the MAC key, MAC algorithm, and other attributes (such as username)\r
+ needed by the application. This function is the equivalent of verifying the username and\r
+ password in Basic authentication.\r
+ \r
+ var credentialsFunc = function (id, callback) {\r
+ \r
+ // Lookup credentials in database\r
+ db.lookup(id, function (err, item) {\r
+ \r
+ if (err || !item) {\r
+ return callback(err);\r
+ }\r
+ \r
+ var credentials = {\r
+ // Required\r
+ key: item.key,\r
+ algorithm: item.algorithm,\r
+ // Application specific\r
+ user: item.user\r
+ };\r
+ \r
+ return callback(null, credentials);\r
+ });\r
+ };\r
+ \r
+ options: {\r
+\r
+ hostHeaderName: optional header field name, used to override the default 'Host' header when used\r
+ behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving\r
+ the original (which is what the module must verify) in the 'x-forwarded-host' header field.\r
+ Only used when passed a node Http.ServerRequest object.\r
+ \r
+ nonceFunc: optional nonce validation function. The function signature is function(nonce, ts, callback)\r
+ where 'callback' must be called using the signature function(err).\r
+ \r
+ timestampSkewSec: optional number of seconds of permitted clock skew for incoming timestamps. Defaults to 60 seconds.\r
+ Provides a +/- skew which means actual allowed window is double the number of seconds.\r
+ \r
+ localtimeOffsetMsec: optional local clock time offset express in a number of milliseconds (positive or negative).\r
+ Defaults to 0.\r
+ \r
+ payload: optional payload for validation. The client calculates the hash value and includes it via the 'hash'\r
+ header attribute. The server always ensures the value provided has been included in the request\r
+ MAC. When this option is provided, it validates the hash value itself. Validation is done by calculating\r
+ a hash value over the entire payload (assuming it has already be normalized to the same format and\r
+ encoding used by the client to calculate the hash on request). If the payload is not available at the time\r
+ of authentication, the authenticatePayload() method can be used by passing it the credentials and\r
+ attributes.hash returned in the authenticate callback.\r
+ }\r
+\r
+ callback: function (err, credentials, artifacts) { }\r
+ */\r
+\r
+exports.authenticate = function (req, credentialsFunc, options, callback) {\r
+\r
+ callback = Utils.nextTick(callback);\r
+ \r
+ // Default options\r
+\r
+ options.nonceFunc = options.nonceFunc || function (nonce, ts, nonceCallback) { return nonceCallback(); }; // No validation\r
+ options.timestampSkewSec = options.timestampSkewSec || 60; // 60 seconds\r
+\r
+ // Application time\r
+\r
+ var now = Utils.now() + (options.localtimeOffsetMsec || 0); // Measure now before any other processing\r
+\r
+ // Convert node Http request object to a request configuration object\r
+\r
+ var request = Utils.parseRequest(req, options);\r
if (request instanceof Error) {\r
return callback(Boom.badRequest(request.message));\r
- }
-
- // Parse HTTP Authorization header
-
- var attributes = Utils.parseAuthorizationHeader(request.authorization);
+ }\r
+\r
+ // Parse HTTP Authorization header\r
+\r
+ var attributes = Utils.parseAuthorizationHeader(request.authorization);\r
if (attributes instanceof Error) {\r
return callback(attributes);\r
- }
-
- // Construct artifacts container
-
+ }\r
+\r
+ // Construct artifacts container\r
+\r
var artifacts = {\r
- 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,
+ method: request.method,\r
+ host: request.host,\r
+ port: request.port,\r
+ resource: request.url,\r
+ ts: attributes.ts,\r
+ nonce: attributes.nonce,\r
+ hash: attributes.hash,\r
+ ext: attributes.ext,\r
+ app: attributes.app,\r
+ dlg: attributes.dlg,\r
+ mac: attributes.mac,\r
id: attributes.id\r
- };
-
- // Verify required header attributes
-
- if (!attributes.id ||
- !attributes.ts ||
- !attributes.nonce ||
+ };\r
+\r
+ // Verify required header attributes\r
+\r
+ if (!attributes.id ||\r
+ !attributes.ts ||\r
+ !attributes.nonce ||\r
!attributes.mac) {\r
-
+\r
return callback(Boom.badRequest('Missing attributes'), null, artifacts);\r
- }
-
- // Fetch Hawk credentials
-
+ }\r
+\r
+ // Fetch Hawk credentials\r
+\r
credentialsFunc(attributes.id, function (err, credentials) {\r
-
- artifacts.credentials = credentials;
-
+\r
if (err) {\r
return callback(err, credentials || null, artifacts);\r
- }
-
+ }\r
+\r
if (!credentials) {\r
return callback(Boom.unauthorized('Unknown credentials', 'Hawk'), null, artifacts);\r
- }
-
- if (!credentials.key ||
+ }\r
+\r
+ if (!credentials.key ||\r
!credentials.algorithm) {\r
-
+\r
return callback(Boom.internal('Invalid credentials'), credentials, artifacts);\r
- }
-
+ }\r
+\r
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
return callback(Boom.internal('Unknown algorithm'), credentials, artifacts);\r
- }
-
- // Calculate MAC
-
- var mac = Crypto.calculateMac('header', artifacts);
+ }\r
+\r
+ // Calculate MAC\r
+\r
+ var mac = Crypto.calculateMac('header', credentials, artifacts);\r
if (!Cryptiles.fixedTimeComparison(mac, attributes.mac)) {\r
return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials, artifacts);\r
- }
-
- // Check payload hash
-
- if (options.payload !== null &&
- options.payload !== undefined) { // '' is valid
-
+ }\r
+\r
+ // Check payload hash\r
+\r
+ if (options.payload !== null &&\r
+ options.payload !== undefined) { // '' is valid\r
+\r
if (!attributes.hash) {\r
return callback(Boom.unauthorized('Missing required payload hash', 'Hawk'), credentials, artifacts);\r
- }
-
- var hash = Crypto.calculateHash(options.payload, credentials.algorithm, request.contentType);
+ }\r
+\r
+ var hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);\r
if (!Cryptiles.fixedTimeComparison(hash, attributes.hash)) {\r
return callback(Boom.unauthorized('Bad payload hash', 'Hawk'), credentials, artifacts);\r
}\r
- }
-
- // Check nonce
-
+ }\r
+\r
+ // Check nonce\r
+\r
options.nonceFunc(attributes.nonce, attributes.ts, function (err) {\r
-
+\r
if (err) {\r
return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials, artifacts);\r
- }
-
- // Check timestamp staleness
-
+ }\r
+\r
+ // Check timestamp staleness\r
+\r
if (Math.abs((attributes.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {\r
- var fresh = Utils.now() + (options.localtimeOffsetMsec || 0); // Get fresh now
- var tsm = Crypto.calculateTsMac(fresh, credentials);
+ var fresh = Math.floor((Utils.now() + (options.localtimeOffsetMsec || 0)) / 1000); // Get fresh now\r
+ var tsm = Crypto.calculateTsMac(fresh, credentials);\r
return callback(Boom.unauthorized('Stale timestamp', 'Hawk', { ts: fresh, tsm: tsm }), credentials, artifacts);\r
- }
-
- // Successful authentication
-
+ }\r
+\r
+ // Successful authentication\r
+\r
return callback(null, credentials, artifacts);\r
});\r
});\r
-};
-
-
-// Authenticate payload hash - used when payload cannot be provided during authenticate()
-
-/*
- payload: raw request payload
- credentials: from authenticate callback
- hash: from authenticate callback (artifacts.hash)
- contentType: req.headers['content-type']
-*/
-
-exports.authenticatePayload = function (payload, credentials, hash, contentType) {\r
-
- var calculatedHash = Crypto.calculateHash(payload, credentials.algorithm, contentType);
- return Cryptiles.fixedTimeComparison(calculatedHash, hash);\r
-};
-
-
-// Generate a Server-Authorization header for a given response
-
-/*
- 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 (artifacts, options) {
-
- // Prepare inputs
-
- options = options || {};
-
- if (!artifacts ||
- typeof artifacts !== 'object' ||
+};\r
+\r
+\r
+// Authenticate payload hash - used when payload cannot be provided during authenticate()\r
+\r
+/*\r
+ payload: raw request payload\r
+ credentials: from authenticate callback\r
+ artifacts: from authenticate callback\r
+ contentType: req.headers['content-type']\r
+*/\r
+\r
+exports.authenticatePayload = function (payload, credentials, artifacts, contentType) {\r
+\r
+ var calculatedHash = Crypto.calculatePayloadHash(payload, credentials.algorithm, contentType);\r
+ return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);\r
+};\r
+\r
+\r
+// Generate a Server-Authorization header for a given response\r
+\r
+/*\r
+ credentials: {}, // Object received from authenticate()\r
+ artifacts: {} // Object received from authenticate(); 'mac', 'hash', and 'ext' - ignored\r
+ options: {\r
+ ext: 'application-specific', // Application specific data sent via the ext attribute\r
+ payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)\r
+ contentType: 'application/json', // Payload content-type (ignored if hash provided)\r
+ hash: 'U4MKKSmiVxk37JCCrAVIjV=' // Pre-calculated payload hash\r
+ }\r
+*/\r
+\r
+exports.header = function (credentials, artifacts, options) {\r
+\r
+ // Prepare inputs\r
+\r
+ options = options || {};\r
+\r
+ if (!artifacts ||\r
+ typeof artifacts !== 'object' ||\r
typeof options !== 'object') {\r
-
+\r
return '';\r
- }
-
- artifacts = Hoek.clone(artifacts);
- delete artifacts.mac;
- artifacts.hash = options.hash;
- artifacts.ext = options.ext;
-
- // Validate credentials
-
- var credentials = artifacts.credentials;
- if (!credentials ||
- !credentials.key ||
- !credentials.algorithm) {
-
- // Invalid credential object
+ }\r
+\r
+ artifacts = Hoek.clone(artifacts);\r
+ delete artifacts.mac;\r
+ artifacts.hash = options.hash;\r
+ artifacts.ext = options.ext;\r
+\r
+ // Validate credentials\r
+\r
+ if (!credentials ||\r
+ !credentials.key ||\r
+ !credentials.algorithm) {\r
+\r
+ // Invalid credential object\r
return '';\r
- }
-
+ }\r
+\r
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
return '';\r
- }
-
- // Calculate payload hash
-
- if (!artifacts.hash &&
+ }\r
+\r
+ // Calculate payload hash\r
+\r
+ if (!artifacts.hash &&\r
options.hasOwnProperty('payload')) {\r
-
- artifacts.hash = Crypto.calculateHash(options.payload, credentials.algorithm, options.contentType);\r
- }
-
- var mac = Crypto.calculateMac('response', 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
-
+\r
+ artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);\r
+ }\r
+\r
+ var mac = Crypto.calculateMac('response', credentials, artifacts);\r
+\r
+ // Construct header\r
+\r
+ var header = 'Hawk mac="' + mac + '"' +\r
+ (artifacts.hash ? ', hash="' + artifacts.hash + '"' : '');\r
+\r
+ if (artifacts.ext !== null &&\r
+ artifacts.ext !== undefined &&\r
+ artifacts.ext !== '') { // Other falsey values allowed\r
+\r
header += ', ext="' + Utils.escapeHeaderAttribute(artifacts.ext) + '"';\r
- }
-
+ }\r
+\r
return header;\r
-};
+};\r
+\r
+\r
+/*\r
+ * Arguments and options are the same as index.js with the exception that the only supported options are:\r
+ * 'hostHeaderName', 'localtimeOffsetMsec'\r
+ */\r
+\r
+exports.authenticateBewit = function (req, credentialsFunc, options, callback) {\r
+\r
+ callback = Utils.nextTick(callback);\r
+\r
+ // Application time\r
+\r
+ var now = Utils.now() + (options.localtimeOffsetMsec || 0);\r
+\r
+ // Convert node Http request object to a request configuration object\r
+\r
+ var request = Utils.parseRequest(req, options);\r
+ if (request instanceof Error) {\r
+ return callback(Boom.badRequest(request.message));\r
+ }\r
+\r
+ // Extract bewit\r
+\r
+ // 1 2 3 4 \r
+ var resource = request.url.match(/^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/);\r
+ if (!resource) {\r
+ return callback(Boom.unauthorized(null, 'Hawk'));\r
+ }\r
+\r
+ // Bewit not empty\r
+\r
+ if (!resource[3]) {\r
+ return callback(Boom.unauthorized('Empty bewit', 'Hawk'));\r
+ }\r
+\r
+ // Verify method is GET\r
+\r
+ if (request.method !== 'GET' &&\r
+ request.method !== 'HEAD') {\r
+\r
+ return callback(Boom.unauthorized('Invalid method', 'Hawk'));\r
+ }\r
+\r
+ // No other authentication\r
+\r
+ if (request.authorization) {\r
+ return callback(Boom.badRequest('Multiple authentications', 'Hawk'));\r
+ }\r
+\r
+ // Parse bewit\r
+\r
+ var bewitString = Utils.base64urlDecode(resource[3]);\r
+ if (bewitString instanceof Error) {\r
+ return callback(Boom.badRequest('Invalid bewit encoding'));\r
+ }\r
+\r
+ // Bewit format: id\exp\mac\ext ('\' is used because it is a reserved header attribute character)\r
+\r
+ var bewitParts = bewitString.split('\\');\r
+ if (!bewitParts ||\r
+ bewitParts.length !== 4) {\r
+\r
+ return callback(Boom.badRequest('Invalid bewit structure'));\r
+ }\r
+\r
+ var bewit = {\r
+ id: bewitParts[0],\r
+ exp: parseInt(bewitParts[1], 10),\r
+ mac: bewitParts[2],\r
+ ext: bewitParts[3] || ''\r
+ };\r
+\r
+ if (!bewit.id ||\r
+ !bewit.exp ||\r
+ !bewit.mac) {\r
+\r
+ return callback(Boom.badRequest('Missing bewit attributes'));\r
+ }\r
+\r
+ // Construct URL without bewit\r
+\r
+ var url = resource[1];\r
+ if (resource[4]) {\r
+ url += resource[2] + resource[4];\r
+ }\r
+\r
+ // Check expiration\r
+\r
+ if (bewit.exp * 1000 <= now) {\r
+ return callback(Boom.unauthorized('Access expired', 'Hawk'), null, bewit);\r
+ }\r
+\r
+ // Fetch Hawk credentials\r
+\r
+ credentialsFunc(bewit.id, function (err, credentials) {\r
+\r
+ if (err) {\r
+ return callback(err, credentials || null, bewit.ext);\r
+ }\r
+\r
+ if (!credentials) {\r
+ return callback(Boom.unauthorized('Unknown credentials', 'Hawk'), null, bewit);\r
+ }\r
+\r
+ if (!credentials.key ||\r
+ !credentials.algorithm) {\r
+\r
+ return callback(Boom.internal('Invalid credentials'), credentials, bewit);\r
+ }\r
+\r
+ if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {\r
+ return callback(Boom.internal('Unknown algorithm'), credentials, bewit);\r
+ }\r
+\r
+ // Calculate MAC\r
+\r
+ var mac = Crypto.calculateMac('bewit', credentials, {\r
+ ts: bewit.exp,\r
+ nonce: '',\r
+ method: 'GET',\r
+ resource: url,\r
+ host: request.host,\r
+ port: request.port,\r
+ ext: bewit.ext\r
+ });\r
+\r
+ if (!Cryptiles.fixedTimeComparison(mac, bewit.mac)) {\r
+ return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials, bewit);\r
+ }\r
+\r
+ // Successful authentication\r
+\r
+ return callback(null, credentials, bewit);\r
+ });\r
+};\r
+++ /dev/null
-// Load modules
-
-var Url = require('url');
-var Boom = require('boom');
-var Cryptiles = require('cryptiles');
-var Crypto = require('./crypto');
-var Utils = require('./utils');
-
-
-// Declare internals
-
-var internals = {};
-
-
-// Hawk authentication
-
-/*
- * Arguments and options are the same as index.js with the exception that the only supported options are:
- * 'hostHeaderName', 'localtimeOffsetMsec'
- */
-
-exports.authenticate = function (req, credentialsFunc, options, callback) {
-
- // Application time
-
- var now = Utils.now() + (options.localtimeOffsetMsec || 0);
-
- // 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', 'Hawk'));
- }
-
- // Parse bewit
-
- var bewitString = Utils.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 ||
- 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: 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);
- });
-};
-
-
-// Generate a bewit value for a given URI
-
-/*
- * credentials is an object with the following keys: 'id, 'key', 'algorithm'.
- * options is an object with the following optional keys: 'ext', 'localtimeOffsetMsec'
- */
-/*
- 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 || 0);
-
- // 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: 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 Utils.base64urlEncode(bewit);
-};
-
-
// Parse Content-Type header content
-exports.parseContentType = function (header) {\r
+exports.parseContentType = function (header) {
- if (!header) {\r
- return '';\r
+ if (!header) {
+ return '';
}
return header.split(';')[0].trim().toLowerCase();
// Parse Hawk HTTP Authorization header
-exports.parseAuthorizationHeader = function (header, keys) {\r
+exports.parseAuthorizationHeader = function (header, keys) {
keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];
- if (!header) {\r
- return Boom.unauthorized(null, 'Hawk');\r
+ if (!header) {
+ return Boom.unauthorized(null, 'Hawk');
}
var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
- if (!headerParts) {\r
- return Boom.badRequest('Invalid header syntax');\r
+ if (!headerParts) {
+ return Boom.badRequest('Invalid header syntax');
}
var scheme = headerParts[1];
- if (scheme.toLowerCase() !== 'hawk') {\r
- return Boom.unauthorized(null, 'Hawk');\r
+ if (scheme.toLowerCase() !== 'hawk') {
+ return Boom.unauthorized(null, 'Hawk');
}
var attributesString = headerParts[2];
- if (!attributesString) {\r
- return Boom.badRequest('Invalid header syntax');\r
+ if (!attributesString) {
+ return Boom.badRequest('Invalid header syntax');
}
var attributes = {};
// Check valid attribute names
- if (keys.indexOf($1) === -1) {\r
+ if (keys.indexOf($1) === -1) {
errorMessage = 'Unknown attribute: ' + $1;
- return;\r
+ return;
}
// Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
- if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {\r
+ if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
errorMessage = 'Bad attribute value: ' + $1;
- return;\r
+ return;
}
// Check for duplicates
- if (attributes.hasOwnProperty($1)) {\r
+ if (attributes.hasOwnProperty($1)) {
errorMessage = 'Duplicate attribute: ' + $1;
- return;\r
+ return;
}
attributes[$1] = $2;
- return '';\r
+ return '';
});
- if (verify !== '') {\r
- return Boom.badRequest(errorMessage || 'Bad header format');\r
- }\r
-\r
- return attributes;\r
+ if (verify !== '') {
+ return Boom.badRequest(errorMessage || 'Bad header format');
+ }
+
+ return attributes;
};
+
+
+exports.unauthorized = function (message) {
+
+ return Boom.unauthorized(message, 'Hawk');
+};
+
language: node_js
node_js:
- - 0.8
+ - 0.10
-// Load modules\r
-\r
-var Http = require('http');\r
-var NodeUtil = require('util');\r
-var Hoek = require('hoek');\r
-\r
-\r
-// Declare internals\r
-\r
-var internals = {};\r
-\r
-\r
-exports = module.exports = internals.Boom = function (/* (new Error) or (code, message) */) {\r
-\r
- var self = this;\r
-\r
- Hoek.assert(this.constructor === internals.Boom, 'Error must be instantiated using new');\r
-\r
- Error.call(this);\r
- this.isBoom = true;\r
-\r
- this.response = {\r
- code: 0,\r
- payload: {},\r
- headers: {}\r
- // type: 'content-type'\r
- };\r
-\r
- if (arguments[0] instanceof Error) {\r
-\r
- // Error\r
-\r
- var error = arguments[0];\r
-\r
- this.data = error;\r
- this.response.code = error.code || 500;\r
- if (error.message) {\r
- this.message = error.message;\r
- }\r
- }\r
- else {\r
-\r
- // code, message\r
-\r
- var code = arguments[0];\r
- var message = arguments[1];\r
-\r
- Hoek.assert(!isNaN(parseFloat(code)) && isFinite(code) && code >= 400, 'First argument must be a number (400+)');\r
-\r
- this.response.code = code;\r
- if (message) {\r
- this.message = message;\r
- }\r
- }\r
-\r
- // Response format\r
-\r
- this.reformat();\r
-\r
- return this;\r
-};\r
-\r
-NodeUtil.inherits(internals.Boom, Error);\r
-\r
-\r
-internals.Boom.prototype.reformat = function () {\r
-\r
- this.response.payload.code = this.response.code;\r
- this.response.payload.error = Http.STATUS_CODES[this.response.code] || 'Unknown';\r
- if (this.message) {\r
- this.response.payload.message = Hoek.escapeHtml(this.message); // Prevent XSS from error message\r
- }\r
-};\r
-\r
-\r
-// Utilities\r
-\r
-internals.Boom.badRequest = function (message) {\r
-\r
- return new internals.Boom(400, message);\r
-};\r
-\r
-\r
-internals.Boom.unauthorized = function (error, scheme, attributes) { // Or function (error, wwwAuthenticate[])\r
-\r
- var err = new internals.Boom(401, error);\r
-\r
- if (!scheme) {\r
- return err;\r
- }\r
-\r
- var wwwAuthenticate = '';\r
-\r
- if (typeof scheme === 'string') {\r
-\r
- // function (error, scheme, attributes)\r
-\r
- wwwAuthenticate = scheme;\r
- if (attributes) {\r
- var names = Object.keys(attributes);\r
- for (var i = 0, il = names.length; i < il; ++i) {\r
- if (i) {\r
- wwwAuthenticate += ',';\r
- }\r
-\r
- var value = attributes[names[i]];\r
- if (value === null ||\r
- value === undefined) { // Value can be zero\r
-\r
- value = '';\r
- }\r
- wwwAuthenticate += ' ' + names[i] + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"';\r
- }\r
- }\r
-\r
- if (error) {\r
- if (attributes) {\r
- wwwAuthenticate += ',';\r
- }\r
- wwwAuthenticate += ' error="' + Hoek.escapeHeaderAttribute(error) + '"';\r
- }\r
- else {\r
- err.isMissing = true;\r
- }\r
- }\r
- else {\r
-\r
- // function (error, wwwAuthenticate[])\r
-\r
- var wwwArray = scheme;\r
- for (var i = 0, il = wwwArray.length; i < il; ++i) {\r
- if (i) {\r
- wwwAuthenticate += ', ';\r
- }\r
-\r
- wwwAuthenticate += wwwArray[i];\r
- }\r
- }\r
-\r
- err.response.headers['WWW-Authenticate'] = wwwAuthenticate;\r
-\r
- return err;\r
-};\r
-\r
-\r
-internals.Boom.clientTimeout = function (message) {\r
-\r
- return new internals.Boom(408, message);\r
-};\r
-\r
-\r
-internals.Boom.serverTimeout = function (message) {\r
-\r
- return new internals.Boom(503, message);\r
-};\r
-\r
-\r
-internals.Boom.forbidden = function (message) {\r
-\r
- return new internals.Boom(403, message);\r
-};\r
-\r
-\r
-internals.Boom.notFound = function (message) {\r
-\r
- return new internals.Boom(404, message);\r
-};\r
-\r
-\r
-internals.Boom.internal = function (message, data) {\r
-\r
- var err = new internals.Boom(500, message);\r
-\r
- if (data && data.stack) {\r
- err.trace = data.stack.split('\n');\r
- err.outterTrace = Hoek.displayStack(1);\r
- }\r
- else {\r
- err.trace = Hoek.displayStack(1);\r
- }\r
-\r
- err.data = data;\r
- err.response.payload.message = 'An internal server error occurred'; // Hide actual error from user\r
-\r
- return err;\r
-};\r
-\r
-\r
-internals.Boom.passThrough = function (code, payload, contentType, headers) {\r
-\r
- var err = new internals.Boom(500, 'Pass-through'); // 500 code is only used to initialize\r
-\r
- err.data = {\r
- code: code,\r
- payload: payload,\r
- type: contentType\r
- };\r
-\r
- err.response.code = code;\r
- err.response.type = contentType;\r
- err.response.headers = headers;\r
- err.response.payload = payload;\r
-\r
- return err;\r
-};\r
-\r
-\r
+// Load modules
+
+var Http = require('http');
+var NodeUtil = require('util');
+var Hoek = require('hoek');
+
+
+// Declare internals
+
+var internals = {};
+
+
+exports = module.exports = internals.Boom = function (/* (new Error) or (code, message) */) {
+
+ var self = this;
+
+ Hoek.assert(this.constructor === internals.Boom, 'Error must be instantiated using new');
+
+ Error.call(this);
+ this.isBoom = true;
+
+ this.response = {
+ code: 0,
+ payload: {},
+ headers: {}
+ // type: 'content-type'
+ };
+
+ if (arguments[0] instanceof Error) {
+
+ // Error
+
+ var error = arguments[0];
+
+ this.data = error;
+ this.response.code = error.code || 500;
+ if (error.message) {
+ this.message = error.message;
+ }
+ }
+ else {
+
+ // code, message
+
+ var code = arguments[0];
+ var message = arguments[1];
+
+ Hoek.assert(!isNaN(parseFloat(code)) && isFinite(code) && code >= 400, 'First argument must be a number (400+)');
+
+ this.response.code = code;
+ if (message) {
+ this.message = message;
+ }
+ }
+
+ // Response format
+
+ this.reformat();
+
+ return this;
+};
+
+NodeUtil.inherits(internals.Boom, Error);
+
+
+internals.Boom.prototype.reformat = function () {
+
+ this.response.payload.code = this.response.code;
+ this.response.payload.error = Http.STATUS_CODES[this.response.code] || 'Unknown';
+ if (this.message) {
+ this.response.payload.message = Hoek.escapeHtml(this.message); // Prevent XSS from error message
+ }
+};
+
+
+// Utilities
+
+internals.Boom.badRequest = function (message) {
+
+ return new internals.Boom(400, message);
+};
+
+
+internals.Boom.unauthorized = function (message, scheme, attributes) { // Or function (message, wwwAuthenticate[])
+
+ var err = new internals.Boom(401, message);
+
+ if (!scheme) {
+ return err;
+ }
+
+ var wwwAuthenticate = '';
+
+ if (typeof scheme === 'string') {
+
+ // function (message, scheme, attributes)
+
+ wwwAuthenticate = scheme;
+ if (attributes) {
+ var names = Object.keys(attributes);
+ for (var 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 (var i = 0, il = wwwArray.length; i < il; ++i) {
+ if (i) {
+ wwwAuthenticate += ', ';
+ }
+
+ wwwAuthenticate += wwwArray[i];
+ }
+ }
+
+ err.response.headers['WWW-Authenticate'] = wwwAuthenticate;
+
+ return err;
+};
+
+
+internals.Boom.clientTimeout = function (message) {
+
+ return new internals.Boom(408, message);
+};
+
+
+internals.Boom.serverTimeout = function (message) {
+
+ return new internals.Boom(503, message);
+};
+
+
+internals.Boom.forbidden = function (message) {
+
+ return new internals.Boom(403, message);
+};
+
+
+internals.Boom.notFound = function (message) {
+
+ return new internals.Boom(404, message);
+};
+
+
+internals.Boom.internal = function (message, data) {
+
+ var err = new internals.Boom(500, message);
+
+ if (data && data.stack) {
+ err.trace = data.stack.split('\n');
+ err.outterTrace = Hoek.displayStack(1);
+ }
+ else {
+ err.trace = Hoek.displayStack(1);
+ }
+
+ err.data = data;
+ err.response.payload.message = 'An internal server error occurred'; // Hide actual error from user
+
+ return err;
+};
+
+
+internals.Boom.passThrough = function (code, payload, contentType, headers) {
+
+ var err = new internals.Boom(500, 'Pass-through'); // 500 code is only used to initialize
+
+ err.data = {
+ code: code,
+ payload: payload,
+ type: contentType
+ };
+
+ err.response.code = code;
+ err.response.type = contentType;
+ err.response.headers = headers;
+ err.response.payload = payload;
+
+ return err;
+};
+
+
{
"name": "boom",
"description": "HTTP-friendly error objects",
- "version": "0.3.8",
+ "version": "0.4.1",
"author": {
"name": "Eran Hammer",
"email": "eran@hueniverse.com",
"http"
],
"engines": {
- "node": "0.8.x"
+ "node": ">=0.8.0"
},
"dependencies": {
- "hoek": "0.7.x"
+ "hoek": "0.8.x"
},
"devDependencies": {
- "lab": "0.0.x",
+ "lab": "0.1.x",
"complexity-report": "0.x.x"
},
"scripts": {
],
"readme": "<a href=\"https://github.com/spumko\"><img src=\"https://raw.github.com/spumko/spumko/master/images/from.png\" align=\"right\" /></a>\n![boom Logo](https://raw.github.com/spumko/boom/master/images/boom.png)\n\nHTTP-friendly error objects\n\n[![Build Status](https://secure.travis-ci.org/spumko/boom.png)](http://travis-ci.org/spumko/boom)\n",
"readmeFilename": "README.md",
- "_id": "boom@0.3.8",
- "dist": {
- "shasum": "1aaf60284b7b0e0dab42ceb8e27afd0e4ab61cdf"
+ "bugs": {
+ "url": "https://github.com/spumko/boom/issues"
},
- "_from": "boom@0.3.x",
- "_resolved": "https://registry.npmjs.org/boom/-/boom-0.3.8.tgz"
+ "_id": "boom@0.4.1",
+ "_from": "boom@0.4.x"
}
-// Load modules\r
-\r
-var Lab = require('lab');\r
-var Boom = require('../lib');\r
-\r
-\r
-// Declare internals\r
-\r
-var internals = {};\r
-\r
-\r
-// Test shortcuts\r
-\r
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
-\r
-\r
-describe('Boom', function () {\r
-\r
- it('returns an error with info when constructed using another error', function (done) {\r
-\r
- var error = new Error('ka-boom');\r
- error.xyz = 123;\r
- var err = new Boom(error);\r
- expect(err.data.xyz).to.equal(123);\r
- expect(err.message).to.equal('ka-boom');\r
- expect(err.response).to.deep.equal({\r
- code: 500,\r
- payload: {\r
- code: 500,\r
- error: 'Internal Server Error',\r
- message: 'ka-boom'\r
- },\r
- headers: {}\r
- });\r
- done();\r
- });\r
-\r
- describe('#isBoom', function () {\r
-\r
- it('returns true for Boom object', function (done) {\r
-\r
- expect(Boom.badRequest().isBoom).to.equal(true);\r
- done();\r
- });\r
-\r
- it('returns false for Error object', function (done) {\r
-\r
- expect(new Error().isBoom).to.not.exist;\r
- done();\r
- });\r
- });\r
-\r
- describe('#badRequest', function () {\r
-\r
- it('returns a 400 error code', function (done) {\r
-\r
- expect(Boom.badRequest().response.code).to.equal(400);\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- expect(Boom.badRequest('my message').message).to.equal('my message');\r
- done();\r
- });\r
- });\r
-\r
- describe('#unauthorized', function () {\r
-\r
- it('returns a 401 error code', function (done) {\r
-\r
- var err = Boom.unauthorized();\r
- expect(err.response.code).to.equal(401);\r
- expect(err.response.headers).to.deep.equal({});\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- expect(Boom.unauthorized('my message').message).to.equal('my message');\r
- done();\r
- });\r
-\r
- it('returns a WWW-Authenticate header when passed a scheme', function (done) {\r
-\r
- var err = Boom.unauthorized('boom', 'Test');\r
- expect(err.response.code).to.equal(401);\r
- expect(err.response.headers['WWW-Authenticate']).to.equal('Test error="boom"');\r
- done();\r
- });\r
-\r
- it('returns a WWW-Authenticate header when passed a scheme and attributes', function (done) {\r
-\r
- var err = Boom.unauthorized('boom', 'Test', { a: 1, b: 'something', c: null, d: 0 });\r
- expect(err.response.code).to.equal(401);\r
- expect(err.response.headers['WWW-Authenticate']).to.equal('Test a="1", b="something", c="", d="0", error="boom"');\r
- done();\r
- });\r
-\r
- it('sets the isMissing flag when error message is empty', function (done) {\r
-\r
- var err = Boom.unauthorized('', 'Basic');\r
- expect(err.isMissing).to.equal(true);\r
- done();\r
- });\r
-\r
- it('does not set the isMissing flag when error message is not empty', function (done) {\r
-\r
- var err = Boom.unauthorized('message', 'Basic');\r
- expect(err.isMissing).to.equal(undefined);\r
- done();\r
- });\r
-\r
- it('sets a WWW-Authenticate when passed as an array', function (done) {\r
-\r
- var err = Boom.unauthorized('message', ['Basic', 'Example e="1"', 'Another x="3", y="4"']);\r
- expect(err.response.headers['WWW-Authenticate']).to.equal('Basic, Example e="1", Another x="3", y="4"');\r
- done();\r
- });\r
- });\r
-\r
- describe('#clientTimeout', function () {\r
-\r
- it('returns a 408 error code', function (done) {\r
-\r
- expect(Boom.clientTimeout().response.code).to.equal(408);\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- expect(Boom.clientTimeout('my message').message).to.equal('my message');\r
- done();\r
- });\r
- });\r
-\r
- describe('#serverTimeout', function () {\r
-\r
- it('returns a 503 error code', function (done) {\r
-\r
- expect(Boom.serverTimeout().response.code).to.equal(503);\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- expect(Boom.serverTimeout('my message').message).to.equal('my message');\r
- done();\r
- });\r
- });\r
-\r
- describe('#forbidden', function () {\r
-\r
- it('returns a 403 error code', function (done) {\r
-\r
- expect(Boom.forbidden().response.code).to.equal(403);\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- expect(Boom.forbidden('my message').message).to.equal('my message');\r
- done();\r
- });\r
- });\r
-\r
- describe('#notFound', function () {\r
-\r
- it('returns a 404 error code', function (done) {\r
-\r
- expect(Boom.notFound().response.code).to.equal(404);\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- expect(Boom.notFound('my message').message).to.equal('my message');\r
- done();\r
- });\r
- });\r
-\r
- describe('#internal', function () {\r
-\r
- it('returns a 500 error code', function (done) {\r
-\r
- expect(Boom.internal().response.code).to.equal(500);\r
- done();\r
- });\r
-\r
- it('sets the message with the passed in message', function (done) {\r
-\r
- var err = Boom.internal('my message');\r
- expect(err.message).to.equal('my message');\r
- expect(err.response.payload.message).to.equal('An internal server error occurred');\r
- done();\r
- });\r
-\r
- it('passes data on the callback if its passed in', function (done) {\r
-\r
- expect(Boom.internal('my message', { my: 'data' }).data.my).to.equal('data');\r
- done();\r
- });\r
-\r
- it('uses passed in stack if its available', function (done) {\r
-\r
- var error = new Error();\r
- error.stack = 'my stack line\nmy second stack line';\r
- expect(Boom.internal('my message', error).trace[0]).to.equal('my stack line');\r
- done();\r
- });\r
- });\r
-\r
- describe('#passThrough', function () {\r
-\r
- it('returns a pass-through error', function (done) {\r
-\r
- var err = Boom.passThrough(499, { a: 1 }, 'application/text', { 'X-Test': 'Boom' });\r
- expect(err.response.code).to.equal(499);\r
- expect(err.message).to.equal('Pass-through');\r
- expect(err.response).to.deep.equal({\r
- code: 499,\r
- payload: { a: 1 },\r
- headers: { 'X-Test': 'Boom' },\r
- type: 'application/text'\r
- });\r
- done();\r
- });\r
- });\r
-\r
- describe('#reformat', function () {\r
-\r
- it('encodes any HTML markup in the response payload', function (done) {\r
-\r
- var boom = new Boom(new Error('<script>alert(1)</script>'));\r
- expect(boom.response.payload.message).to.not.contain('<script>');\r
- done();\r
- });\r
- });\r
-});\r
-\r
-\r
+// Load modules
+
+var Lab = require('lab');
+var Boom = require('../lib');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
+
+
+describe('Boom', function () {
+
+ it('returns an error with info when constructed using another error', function (done) {
+
+ var error = new Error('ka-boom');
+ error.xyz = 123;
+ var err = new Boom(error);
+ expect(err.data.xyz).to.equal(123);
+ expect(err.message).to.equal('ka-boom');
+ expect(err.response).to.deep.equal({
+ code: 500,
+ payload: {
+ code: 500,
+ error: 'Internal Server Error',
+ message: 'ka-boom'
+ },
+ headers: {}
+ });
+ 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 code', function (done) {
+
+ expect(Boom.badRequest().response.code).to.equal(400);
+ done();
+ });
+
+ it('sets the message with the passed in message', function (done) {
+
+ expect(Boom.badRequest('my message').message).to.equal('my message');
+ done();
+ });
+ });
+
+ describe('#unauthorized', function () {
+
+ it('returns a 401 error code', function (done) {
+
+ var err = Boom.unauthorized();
+ expect(err.response.code).to.equal(401);
+ expect(err.response.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.response.code).to.equal(401);
+ expect(err.response.headers['WWW-Authenticate']).to.equal('Test error="boom"');
+ 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.response.code).to.equal(401);
+ expect(err.response.headers['WWW-Authenticate']).to.equal('Test a="1", b="something", c="", d="0", error="boom"');
+ 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.response.headers['WWW-Authenticate']).to.equal('Basic, Example e="1", Another x="3", y="4"');
+ done();
+ });
+ });
+
+ describe('#clientTimeout', function () {
+
+ it('returns a 408 error code', function (done) {
+
+ expect(Boom.clientTimeout().response.code).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('#serverTimeout', function () {
+
+ it('returns a 503 error code', function (done) {
+
+ expect(Boom.serverTimeout().response.code).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 code', function (done) {
+
+ expect(Boom.forbidden().response.code).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 code', function (done) {
+
+ expect(Boom.notFound().response.code).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 code', function (done) {
+
+ expect(Boom.internal().response.code).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.response.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('uses passed in stack if its available', function (done) {
+
+ var error = new Error();
+ error.stack = 'my stack line\nmy second stack line';
+ expect(Boom.internal('my message', error).trace[0]).to.equal('my stack line');
+ done();
+ });
+ });
+
+ describe('#passThrough', function () {
+
+ it('returns a pass-through error', function (done) {
+
+ var err = Boom.passThrough(499, { a: 1 }, 'application/text', { 'X-Test': 'Boom' });
+ expect(err.response.code).to.equal(499);
+ expect(err.message).to.equal('Pass-through');
+ expect(err.response).to.deep.equal({
+ code: 499,
+ payload: { a: 1 },
+ headers: { 'X-Test': 'Boom' },
+ type: 'application/text'
+ });
+ done();
+ });
+ });
+
+ describe('#reformat', function () {
+
+ it('encodes any HTML markup in the response payload', function (done) {
+
+ var boom = new Boom(new Error('<script>alert(1)</script>'));
+ expect(boom.response.payload.message).to.not.contain('<script>');
+ done();
+ });
+ });
+});
+
+
language: node_js
node_js:
- - 0.8
+ - 0.10
-test:\r
- @./node_modules/.bin/lab\r
-test-cov: \r
- @./node_modules/.bin/lab -r threshold -t 100\r
-test-cov-html:\r
- @./node_modules/.bin/lab -r html -o coverage.html\r
-complexity:\r
- @./node_modules/.bin/cr -o complexity.md -f markdown lib\r
-\r
-.PHONY: test test-cov test-cov-html complexity\r
-\r
+test:
+ @./node_modules/.bin/lab
+test-cov:
+ @./node_modules/.bin/lab -r threshold -t 100
+test-cov-html:
+ @./node_modules/.bin/lab -r html -o coverage.html
+complexity:
+ @./node_modules/.bin/cr -o complexity.md -f markdown lib
+
+.PHONY: test test-cov test-cov-html complexity
+
{
"name": "cryptiles",
"description": "General purpose crypto utilities",
- "version": "0.1.3",
+ "version": "0.2.1",
"author": {
"name": "Eran Hammer",
"email": "eran@hueniverse.com",
"utilites"
],
"engines": {
- "node": "0.8.x"
+ "node": ">=0.8.0"
},
"dependencies": {
- "boom": "0.3.x"
+ "boom": "0.4.x"
},
"devDependencies": {
- "lab": "0.0.x",
+ "lab": "0.1.x",
"complexity-report": "0.x.x"
},
"scripts": {
],
"readme": "cryptiles\n=========\n\nGeneral purpose crypto utilities\n\n[![Build Status](https://secure.travis-ci.org/hueniverse/cryptiles.png)](http://travis-ci.org/hueniverse/cryptiles)\n",
"readmeFilename": "README.md",
- "_id": "cryptiles@0.1.3",
- "dist": {
- "shasum": "dfc70d32f10b0a09654ab8bac5fc659b36dd73f1"
+ "bugs": {
+ "url": "https://github.com/hueniverse/cryptiles/issues"
},
- "_from": "cryptiles@0.1.x",
- "_resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.1.3.tgz"
+ "_id": "cryptiles@0.2.1",
+ "_from": "cryptiles@0.2.x"
}
// Load modules
-var Lab = require('lab');\r
+var Lab = require('lab');
var Cryptiles = require('../lib');
// Test shortcuts
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
describe('Cryptiles', function () {
language: node_js
node_js:
- - 0.8
+ - 0.10
-<a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a>
-![hoek Logo](https://raw.github.com/spumko/hoek/master/images/hoek.png)
-
-General purpose node utilities
-
-[![Build Status](https://secure.travis-ci.org/spumko/hoek.png)](http://travis-ci.org/spumko/hoek)
-
-# Table of Contents
-
-* [Introduction](#introduction "Introduction")
-* [Object](#object "Object")
- * [clone](#cloneobj "clone")
- * [merge](#mergetarget-source-isnulloverride-ismergearrays "merge")
- * [applyToDefaults](#applytodefaultsdefaults-options "applyToDefaults")
- * [unique](#uniquearray-key "unique")
- * [mapToObject](#maptoobjectarray-key "mapToObject")
- * [intersect](#intersectarray1-array2 "intersect")
- * [matchKeys](#matchkeysobj-keys "matchKeys")
- * [flatten](#flattenarray-target "flatten")
- * [removeKeys](#removekeysobject-keys "removeKeys")
- * [reach](#reachobj-chain "reach")
- * [inheritAsync](#inheritasyncself-obj-keys "inheritAsync")
- * [rename](#renameobj-from-to "rename")
-* [Timer](#timer "Timer")
-* [Binary Encoding/Decoding](#binary "Binary Encoding/Decoding")
- * [base64urlEncode](#binary64urlEncodevalue "binary64urlEncode")
- * [base64urlDecode](#binary64urlDecodevalue "binary64urlDecode")
-* [Escaping Characters](#escaped "Escaping Characters")
- * [escapeHtml](#escapeHtmlstring "escapeHtml")
- * [escapeHeaderAttribute](#escapeHeaderAttributeattribute "escapeHeaderAttribute")
- * [escapeRegex](#escapeRegexstring "escapeRegex")
-* [Errors](#errors "Errors")
- * [assert](#assertmessage "assert")
- * [abort](#abortmessage "abort")
- * [displayStack](#displayStackslice "displayStack")
- * [callStack](#callStackslice "callStack")
- * [toss](#tosscondition "toss")
-* [Load files](#load-files "Load Files")
- * [loadPackage](#loadPackagedir "loadpackage")
- * [loadDirModules](#loadDirModulespath-excludefiles-target "loaddirmodules")
-
-
-
-# Introduction
-
-The *Hoek* general purpose node utilities library is used to aid in a variety of manners. It comes with useful methods for Ararys (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).
-
-```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
-```
-
-### merge(target, source, isNullOverride, isMergeArrays)
-isNullOverride, isMergeArrays default to true
-
-Merge all the properties of source into target, source wins in conflic, and by default null and undefined from source are applied
-
-
-```javascript
-
-var target = {a: 1, b : 2}
-var source = {a: 0, c: 5}
-var source2 = {a: null, c: 5}
-
-var targetArray = [1, 2, 3];
-var sourceArray = [4, 5];
-
-var newTarget = Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5}
-newTarget = Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5}
-newTarget = Hoek.merge(target, source2, false); // results in {a: null, b:2, c: 5}
-
-newTarget = Hoek.merge(targetArray, sourceArray) // results in [1, 2, 3, 4, 5]
-newTarget = Hoek.merge(targetArray, sourceArray, true, false) // results in [4, 5]
-
-
-
-
-```
-
-### applyToDefaults(defaults, options)
-
-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};
-
-
-```
-
-### 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]
-
-```
-
-### matchKeys(obj, keys)
-
-Find which keys are present
-
-```javascript
-
-var obj = {a: 1, b: 2, c: 3};
-var keys = ["a", "e"];
-
-Hoek.matchKeys(obj, keys) // returns ["a"]
-
-```
-
-### flatten(array, target)
-
-Flatten an array
-
-```javascript
-
-var array = [1, 2, 3];
-var target = [4, 5];
-
-var flattenedArray = Hoek.flatten(array, target) // results in [4, 5, 1, 2, 3];
-
-```
-
-### removeKeys(object, keys)
-
-Remove keys
-
-```javascript
-
-var object = {a: 1, b: 2, c: 3, d: 4};
-
-var keys = ["a", "b"];
-
-Hoek.removeKeys(object, keys) // object is now {c: 3, d: 4}
-
-```
-
-### reach(obj, chain)
-
-Converts an object key chain string to reference
-
-```javascript
-
-var chain = 'a.b.c';
-var obj = {a : {b : { c : 1}}};
-
-Hoek.reach(obj, chain) // returns 1
-
-```
-
-### inheritAsync(self, obj, keys)
-
-Inherits a selected set of methods from an object, wrapping functions in asynchronous syntax and catching errors
-
-```javascript
-
-var targetFunc = function () { };
-
-var proto = {
- a: function () {
- return 'a!';
- },
- b: function () {
- return 'b!';
- },
- c: function () {
- throw new Error('c!');
- }
- };
-
-var keys = ['a', 'c'];
-
-Hoek.inheritAsync(targetFunc, proto, ['a', 'c']);
-
-var target = new targetFunc();
-
-target.a(function(err, result){console.log(result)} // returns 'a!'
-
-target.c(function(err, result){console.log(result)} // returns undefined
-
-target.b(function(err, result){console.log(result)} // gives error: Object [object Object] has no method 'b'
-
-```
-
-### rename(obj, from, to)
-
-Rename a key of an object
-
-```javascript
-
-var obj = {a : 1, b : 2};
-
-Hoek.rename(obj, "a", "c"); // obj is now {c : 1, b : 2}
-
-```
-
-
-# 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
-
-
-example :
-
-
-var timerObj = new Hoek.Timer();
-console.log("Time is now: " + timerObj.ts)
-console.log("Elapsed time from initialization: " + timerObj.elapsed() + 'milliseconds')
-
-```
-
-# 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(message)
-
-```javascript
-
-var a = 1, b =2;
-
-Hoek.assert(a === b, 'a should equal b'); // ABORT: a should equal b
-
-```
-
-### 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 ] ]
-
-
-```
-
-### toss(condition)
-
-toss(condition /*, [message], callback */)
-
-Return an error as first argument of a callback
-
-
-# Load Files
-
-### loadPackage(dir)
-
-Load and parse package.json process root or given directory
-
-```javascript
-
-var pack = Hoek.loadPackage(); // pack.name === 'hoek'
-
-```
-
-### loadDirModules(path, excludeFiles, target)
-
-Loads modules from a given path; option to exclude files (array).
-
-
-
-
+<a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a>\r
+![hoek Logo](https://raw.github.com/spumko/hoek/master/images/hoek.png)\r
+\r
+General purpose node utilities\r
+\r
+[![Build Status](https://secure.travis-ci.org/spumko/hoek.png)](http://travis-ci.org/spumko/hoek)\r
+\r
+# Table of Contents\r
+\r
+* [Introduction](#introduction "Introduction")\r
+* [Object](#object "Object")\r
+ * [clone](#cloneobj "clone")\r
+ * [merge](#mergetarget-source-isnulloverride-ismergearrays "merge")\r
+ * [applyToDefaults](#applytodefaultsdefaults-options "applyToDefaults")\r
+ * [unique](#uniquearray-key "unique")\r
+ * [mapToObject](#maptoobjectarray-key "mapToObject")\r
+ * [intersect](#intersectarray1-array2 "intersect")\r
+ * [matchKeys](#matchkeysobj-keys "matchKeys")\r
+ * [flatten](#flattenarray-target "flatten")\r
+ * [removeKeys](#removekeysobject-keys "removeKeys")\r
+ * [reach](#reachobj-chain "reach")\r
+ * [inheritAsync](#inheritasyncself-obj-keys "inheritAsync")\r
+ * [rename](#renameobj-from-to "rename")\r
+* [Timer](#timer "Timer")\r
+* [Binary Encoding/Decoding](#binary "Binary Encoding/Decoding")\r
+ * [base64urlEncode](#binary64urlEncodevalue "binary64urlEncode")\r
+ * [base64urlDecode](#binary64urlDecodevalue "binary64urlDecode")\r
+* [Escaping Characters](#escaped "Escaping Characters")\r
+ * [escapeHtml](#escapeHtmlstring "escapeHtml")\r
+ * [escapeHeaderAttribute](#escapeHeaderAttributeattribute "escapeHeaderAttribute")\r
+ * [escapeRegex](#escapeRegexstring "escapeRegex")\r
+* [Errors](#errors "Errors")\r
+ * [assert](#assertmessage "assert")\r
+ * [abort](#abortmessage "abort")\r
+ * [displayStack](#displayStackslice "displayStack")\r
+ * [callStack](#callStackslice "callStack")\r
+ * [toss](#tosscondition "toss")\r
+* [Load files](#load-files "Load Files")\r
+ * [loadPackage](#loadPackagedir "loadpackage")\r
+ * [loadDirModules](#loadDirModulespath-excludefiles-target "loaddirmodules")\r
+\r
+\r
+\r
+# Introduction\r
+\r
+The *Hoek* general purpose node utilities library is used to aid in a variety of manners. It comes with useful methods for Arrays (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more. \r
+\r
+For example, to use Hoek to set configuration with default options:\r
+```javascript\r
+var Hoek = require('hoek');\r
+\r
+var default = {url : "www.github.com", port : "8000", debug : true}\r
+\r
+var config = Hoek.applyToDefaults(default, {port : "3000", admin : true});\r
+\r
+// In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true }\r
+```\r
+\r
+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.\r
+\r
+## Object\r
+\r
+Hoek provides several helpful methods for objects and arrays.\r
+\r
+### clone(obj)\r
+\r
+This method is used to clone an object or an array. A *deep copy* is made (duplicates everything, including values that are objects). \r
+\r
+```javascript\r
+\r
+var nestedObj = {\r
+ w: /^something$/ig,\r
+ x: {\r
+ a: [1, 2, 3],\r
+ b: 123456,\r
+ c: new Date()\r
+ },\r
+ y: 'y',\r
+ z: new Date()\r
+ };\r
+\r
+var copy = Hoek.clone(nestedObj);\r
+\r
+copy.x.b = 100;\r
+\r
+console.log(copy.y) // results in 'y'\r
+console.log(nestedObj.x.b) // results in 123456\r
+console.log(copy.x.b) // results in 100\r
+```\r
+\r
+### merge(target, source, isNullOverride, isMergeArrays)\r
+isNullOverride, isMergeArrays default to true\r
+\r
+Merge all the properties of source into target, source wins in conflic, and by default null and undefined from source are applied\r
+\r
+\r
+```javascript\r
+\r
+var target = {a: 1, b : 2}\r
+var source = {a: 0, c: 5}\r
+var source2 = {a: null, c: 5}\r
+\r
+var targetArray = [1, 2, 3];\r
+var sourceArray = [4, 5];\r
+\r
+var newTarget = Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5}\r
+newTarget = Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5}\r
+newTarget = Hoek.merge(target, source2, false); // results in {a: 1, b: 2, c: 5}\r
+\r
+newTarget = Hoek.merge(targetArray, sourceArray) // results in [1, 2, 3, 4, 5]\r
+newTarget = Hoek.merge(targetArray, sourceArray, true, false) // results in [4, 5]\r
+\r
+\r
+\r
+\r
+```\r
+\r
+### applyToDefaults(defaults, options)\r
+\r
+Apply options to a copy of the defaults\r
+\r
+```javascript\r
+\r
+var defaults = {host: "localhost", port: 8000};\r
+var options = {port: 8080};\r
+\r
+var config = Hoek.applyToDefaults(defaults, options); // results in {host: "localhost", port: 8080};\r
+\r
+\r
+```\r
+\r
+### unique(array, key)\r
+\r
+Remove duplicate items from Array\r
+\r
+```javascript\r
+\r
+var array = [1, 2, 2, 3, 3, 4, 5, 6];\r
+\r
+var newArray = Hoek.unique(array); // results in [1,2,3,4,5,6];\r
+\r
+array = [{id: 1}, {id: 1}, {id: 2}];\r
+\r
+newArray = Hoek.unique(array, "id") // results in [{id: 1}, {id: 2}]\r
+\r
+```\r
+\r
+### mapToObject(array, key)\r
+\r
+Convert an Array into an Object\r
+\r
+```javascript\r
+\r
+var array = [1,2,3];\r
+var newObject = Hoek.mapToObject(array); // results in [{"1": true}, {"2": true}, {"3": true}]\r
+\r
+array = [{id: 1}, {id: 2}];\r
+newObject = Hoek.mapToObject(array, "id") // results in [{"id": 1}, {"id": 2}]\r
+\r
+```\r
+### intersect(array1, array2)\r
+\r
+Find the common unique items in two arrays\r
+\r
+```javascript\r
+\r
+var array1 = [1, 2, 3];\r
+var array2 = [1, 4, 5];\r
+\r
+var newArray = Hoek.intersect(array1, array2) // results in [1]\r
+\r
+```\r
+\r
+### matchKeys(obj, keys) \r
+\r
+Find which keys are present\r
+\r
+```javascript\r
+\r
+var obj = {a: 1, b: 2, c: 3};\r
+var keys = ["a", "e"];\r
+\r
+Hoek.matchKeys(obj, keys) // returns ["a"]\r
+\r
+```\r
+\r
+### flatten(array, target)\r
+\r
+Flatten an array\r
+\r
+```javascript\r
+\r
+var array = [1, 2, 3];\r
+var target = [4, 5]; \r
+\r
+var flattenedArray = Hoek.flatten(array, target) // results in [4, 5, 1, 2, 3];\r
+\r
+```\r
+\r
+### removeKeys(object, keys)\r
+\r
+Remove keys\r
+\r
+```javascript\r
+\r
+var object = {a: 1, b: 2, c: 3, d: 4};\r
+\r
+var keys = ["a", "b"];\r
+\r
+Hoek.removeKeys(object, keys) // object is now {c: 3, d: 4}\r
+\r
+```\r
+\r
+### reach(obj, chain)\r
+\r
+Converts an object key chain string to reference\r
+\r
+```javascript\r
+\r
+var chain = 'a.b.c';\r
+var obj = {a : {b : { c : 1}}};\r
+\r
+Hoek.reach(obj, chain) // returns 1\r
+\r
+```\r
+\r
+### inheritAsync(self, obj, keys) \r
+\r
+Inherits a selected set of methods from an object, wrapping functions in asynchronous syntax and catching errors\r
+\r
+```javascript\r
+\r
+var targetFunc = function () { };\r
+\r
+var proto = {\r
+ a: function () {\r
+ return 'a!';\r
+ },\r
+ b: function () {\r
+ return 'b!';\r
+ },\r
+ c: function () {\r
+ throw new Error('c!');\r
+ }\r
+ };\r
+\r
+var keys = ['a', 'c'];\r
+\r
+Hoek.inheritAsync(targetFunc, proto, ['a', 'c']);\r
+\r
+var target = new targetFunc();\r
+\r
+target.a(function(err, result){console.log(result)} // returns 'a!' \r
+\r
+target.c(function(err, result){console.log(result)} // returns undefined\r
+\r
+target.b(function(err, result){console.log(result)} // gives error: Object [object Object] has no method 'b'\r
+\r
+```\r
+\r
+### rename(obj, from, to)\r
+\r
+Rename a key of an object\r
+\r
+```javascript\r
+\r
+var obj = {a : 1, b : 2};\r
+\r
+Hoek.rename(obj, "a", "c"); // obj is now {c : 1, b : 2}\r
+\r
+```\r
+\r
+\r
+# Timer\r
+\r
+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.\r
+\r
+```javascript\r
+\r
+\r
+example : \r
+\r
+\r
+var timerObj = new Hoek.Timer();\r
+console.log("Time is now: " + timerObj.ts)\r
+console.log("Elapsed time from initialization: " + timerObj.elapsed() + 'milliseconds')\r
+\r
+```\r
+\r
+# Binary Encoding/Decoding\r
+\r
+### base64urlEncode(value)\r
+\r
+Encodes value in Base64 or URL encoding\r
+\r
+### base64urlDecode(value)\r
+\r
+Decodes data in Base64 or URL encoding.\r
+# Escaping Characters\r
+\r
+Hoek provides convenient methods for escaping html characters. The escaped characters are as followed:\r
+\r
+```javascript\r
+\r
+internals.htmlEscaped = {\r
+ '&': '&',\r
+ '<': '<',\r
+ '>': '>',\r
+ '"': '"',\r
+ "'": ''',\r
+ '`': '`'\r
+};\r
+\r
+```\r
+\r
+### escapeHtml(string)\r
+\r
+```javascript\r
+\r
+var string = '<html> hey </html>';\r
+var escapedString = Hoek.escapeHtml(string); // returns <html> hey </html>\r
+\r
+```\r
+\r
+### escapeHeaderAttribute(attribute)\r
+\r
+Escape attribute value for use in HTTP header\r
+\r
+```javascript\r
+\r
+var a = Hoek.escapeHeaderAttribute('I said "go w\\o me"'); //returns I said \"go w\\o me\"\r
+\r
+\r
+```\r
+\r
+\r
+### escapeRegex(string)\r
+\r
+Escape string for Regex construction\r
+\r
+```javascript\r
+\r
+var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); // returns 4\^f\$s\.4\*5\+\-_\?%\=#\!\:@\|~\\\/`"\(>\)\[<\]d\{\}s\,\r
+\r
+\r
+\r
+```\r
+\r
+# Errors\r
+\r
+### assert(message)\r
+\r
+```javascript\r
+\r
+var a = 1, b =2;\r
+\r
+Hoek.assert(a === b, 'a should equal b'); // ABORT: a should equal b\r
+\r
+```\r
+\r
+### abort(message)\r
+\r
+First checks if process.env.NODE_ENV === 'test', and if so, throws error message. Otherwise,\r
+displays most recent stack and then exits process.\r
+\r
+\r
+\r
+### displayStack(slice)\r
+\r
+Displays the trace stack\r
+\r
+```javascript\r
+\r
+var stack = Hoek.displayStack();\r
+console.log(stack) // returns something like:\r
+\r
+[ 'null (/Users/user/Desktop/hoek/test.js:4:18)',\r
+ 'Module._compile (module.js:449:26)',\r
+ 'Module._extensions..js (module.js:467:10)',\r
+ 'Module.load (module.js:356:32)',\r
+ 'Module._load (module.js:312:12)',\r
+ 'Module.runMain (module.js:492:10)',\r
+ 'startup.processNextTick.process._tickCallback (node.js:244:9)' ]\r
+\r
+```\r
+\r
+### callStack(slice)\r
+\r
+Returns a trace stack array.\r
+\r
+```javascript\r
+\r
+var stack = Hoek.callStack();\r
+console.log(stack) // returns something like:\r
+\r
+[ [ '/Users/user/Desktop/hoek/test.js', 4, 18, null, false ],\r
+ [ 'module.js', 449, 26, 'Module._compile', false ],\r
+ [ 'module.js', 467, 10, 'Module._extensions..js', false ],\r
+ [ 'module.js', 356, 32, 'Module.load', false ],\r
+ [ 'module.js', 312, 12, 'Module._load', false ],\r
+ [ 'module.js', 492, 10, 'Module.runMain', false ],\r
+ [ 'node.js',\r
+ 244,\r
+ 9,\r
+ 'startup.processNextTick.process._tickCallback',\r
+ false ] ]\r
+\r
+\r
+```\r
+\r
+### toss(condition)\r
+\r
+toss(condition /*, [message], callback */)\r
+\r
+Return an error as first argument of a callback\r
+\r
+\r
+# Load Files\r
+\r
+### loadPackage(dir)\r
+\r
+Load and parse package.json process root or given directory\r
+\r
+```javascript\r
+\r
+var pack = Hoek.loadPackage(); // pack.name === 'hoek'\r
+\r
+```\r
+\r
+### loadDirModules(path, excludeFiles, target) \r
+\r
+Loads modules from a given path; option to exclude files (array).\r
+\r
+\r
+\r
+\r
-// Declare internals\r
-\r
-var internals = {};\r
-\r
-\r
-exports.escapeJavaScript = function (input) {\r
-\r
- if (!input) {\r
- return '';\r
- }\r
-\r
- var escaped = '';\r
-\r
- for (var i = 0, il = input.length; i < il; ++i) {\r
-\r
- var charCode = input.charCodeAt(i);\r
-\r
- if (internals.isSafe(charCode)) {\r
- escaped += input[i];\r
- }\r
- else {\r
- escaped += internals.escapeJavaScriptChar(charCode);\r
- }\r
- }\r
-\r
- return escaped;\r
-};\r
-\r
-\r
-exports.escapeHtml = function (input) {\r
-\r
- if (!input) {\r
- return '';\r
- }\r
-\r
- var escaped = '';\r
-\r
- for (var i = 0, il = input.length; i < il; ++i) {\r
-\r
- var charCode = input.charCodeAt(i);\r
-\r
- if (internals.isSafe(charCode)) {\r
- escaped += input[i];\r
- }\r
- else {\r
- escaped += internals.escapeHtmlChar(charCode);\r
- }\r
- }\r
-\r
- return escaped;\r
-};\r
-\r
-\r
-internals.escapeJavaScriptChar = function (charCode) {\r
-\r
- if (charCode >= 256) {\r
- return '\\u' + internals.padLeft('' + charCode, 4);\r
- }\r
-\r
- var hexValue = new Buffer(String.fromCharCode(charCode), 'ascii').toString('hex');\r
- return '\\x' + internals.padLeft(hexValue, 2);\r
-};\r
-\r
-\r
-internals.escapeHtmlChar = function (charCode) {\r
-\r
- var namedEscape = internals.namedHtml[charCode];\r
- if (typeof namedEscape !== 'undefined') {\r
- return namedEscape;\r
- }\r
-\r
- if (charCode >= 256) {\r
- return '&#' + charCode + ';';\r
- }\r
-\r
- var hexValue = new Buffer(String.fromCharCode(charCode), 'ascii').toString('hex');\r
- return '&#x' + internals.padLeft(hexValue, 2) + ';';\r
-};\r
-\r
-\r
-internals.padLeft = function (str, len) {\r
-\r
- while (str.length < len) {\r
- str = '0' + str;\r
- }\r
-\r
- return str;\r
-};\r
-\r
-\r
-internals.isSafe = function (charCode) {\r
-\r
- return (typeof internals.safeCharCodes[charCode] !== 'undefined');\r
-};\r
-\r
-\r
-internals.namedHtml = {\r
- '38': '&',\r
- '60': '<',\r
- '62': '>',\r
- '34': '"',\r
- '160': ' ',\r
- '162': '¢',\r
- '163': '£',\r
- '164': '¤',\r
- '169': '©',\r
- '174': '®'\r
-};\r
-\r
-\r
-internals.safeCharCodes = (function () {\r
-\r
- var safe = {};\r
-\r
- for (var i = 32; i < 123; ++i) {\r
-\r
- if ((i >= 97 && i <= 122) || // a-z\r
- (i >= 65 && i <= 90) || // A-Z\r
- (i >= 48 && i <= 57) || // 0-9\r
- i === 32 || // space\r
- i === 46 || // .\r
- i === 44 || // ,\r
- i === 45 || // -\r
- i === 58 || // :\r
- i === 95) { // _\r
-\r
- safe[i] = null;\r
- }\r
- }\r
-\r
- return safe;\r
+// 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 && i <= 122) || // 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;
}());
\ No newline at end of file
\r
for (var i in obj) {\r
if (obj.hasOwnProperty(i)) {\r
- if (obj[i] instanceof Date) {\r
+ if (obj[i] instanceof Buffer) {\r
+ newObj[i] = new Buffer(obj[i]);\r
+ }\r
+ else if (obj[i] instanceof Date) {\r
newObj[i] = new Date(obj[i].getTime());\r
}\r
else if (obj[i] instanceof RegExp) {\r
\r
self.prototype[i] = (function (fn) {\r
\r
- return function (callback) {\r
+ return function (next) {\r
\r
var result = null;\r
try {\r
result = fn();\r
}\r
catch (err) {\r
- return callback(err);\r
+ return next(err);\r
}\r
\r
- return callback(null, result);\r
+ return next(null, result);\r
};\r
})(obj[i]);\r
}\r
};\r
\r
\r
-exports.assert = function (condition, message) {\r
+exports.assert = function (condition /*, msg1, msg2, msg3 */) {\r
\r
- if (!condition) {\r
- throw new Error(message || 'Unknown error');\r
+ if (condition) {\r
+ return;\r
}\r
+\r
+ var msgs = Array.prototype.slice.call(arguments, 1);\r
+ msgs = msgs.map(function (msg) {\r
+\r
+ return typeof msg === 'string' ? msg : msg instanceof Error ? msg.message : JSON.stringify(msg);\r
+ });\r
+ throw new Error(msgs.join(' ') || 'Unknown error');\r
};\r
\r
\r
\r
// Return an error as first argument of a callback\r
\r
-exports.toss = function (condition /*, [message], callback */) {\r
+exports.toss = function (condition /*, [message], next */) {\r
\r
var message = (arguments.length === 3 ? arguments[1] : '');\r
- var callback = (arguments.length === 3 ? arguments[2] : arguments[1]);\r
+ var next = (arguments.length === 3 ? arguments[2] : arguments[1]);\r
\r
var err = (message instanceof Error ? message : (message ? new Error(message) : (condition instanceof Error ? condition : new Error())));\r
\r
if (condition instanceof Error ||\r
!condition) {\r
\r
- return callback(err);\r
+ return next(err);\r
}\r
};\r
\r
var output = timestring + ', ' + event.tags[0] + ', ' + data;\r
exports.consoleFunc(output);\r
};\r
+\r
+\r
+exports.nextTick = function (callback) {\r
+\r
+ return function () {\r
+\r
+ var args = arguments;\r
+ process.nextTick(function () {\r
+\r
+ callback.apply(null, args);\r
+ });\r
+ };\r
+};\r
{
"name": "hoek",
"description": "General purpose node utilities",
- "version": "0.7.6",
+ "version": "0.8.5",
"author": {
"name": "Eran Hammer",
"email": "eran@hueniverse.com",
"utilities"
],
"engines": {
- "node": "0.8.x"
+ "node": ">=0.8.0"
},
"dependencies": {},
"devDependencies": {
- "lab": "0.0.x",
+ "lab": "0.1.x",
"complexity-report": "0.x.x"
},
"scripts": {
"url": "http://github.com/spumko/hoek/raw/master/LICENSE"
}
],
- "readme": "<a href=\"https://github.com/spumko\"><img src=\"https://raw.github.com/spumko/spumko/master/images/from.png\" align=\"right\" /></a>\n![hoek Logo](https://raw.github.com/spumko/hoek/master/images/hoek.png)\n\nGeneral purpose node utilities\n\n[![Build Status](https://secure.travis-ci.org/spumko/hoek.png)](http://travis-ci.org/spumko/hoek)\n\n# Table of Contents\n\n* [Introduction](#introduction \"Introduction\")\n* [Object](#object \"Object\")\n * [clone](#cloneobj \"clone\")\n * [merge](#mergetarget-source-isnulloverride-ismergearrays \"merge\")\n * [applyToDefaults](#applytodefaultsdefaults-options \"applyToDefaults\")\n * [unique](#uniquearray-key \"unique\")\n * [mapToObject](#maptoobjectarray-key \"mapToObject\")\n * [intersect](#intersectarray1-array2 \"intersect\")\n * [matchKeys](#matchkeysobj-keys \"matchKeys\")\n * [flatten](#flattenarray-target \"flatten\")\n * [removeKeys](#removekeysobject-keys \"removeKeys\")\n * [reach](#reachobj-chain \"reach\")\n * [inheritAsync](#inheritasyncself-obj-keys \"inheritAsync\")\n * [rename](#renameobj-from-to \"rename\")\n* [Timer](#timer \"Timer\")\n* [Binary Encoding/Decoding](#binary \"Binary Encoding/Decoding\")\n * [base64urlEncode](#binary64urlEncodevalue \"binary64urlEncode\")\n * [base64urlDecode](#binary64urlDecodevalue \"binary64urlDecode\")\n* [Escaping Characters](#escaped \"Escaping Characters\")\n * [escapeHtml](#escapeHtmlstring \"escapeHtml\")\n * [escapeHeaderAttribute](#escapeHeaderAttributeattribute \"escapeHeaderAttribute\")\n * [escapeRegex](#escapeRegexstring \"escapeRegex\")\n* [Errors](#errors \"Errors\")\n * [assert](#assertmessage \"assert\")\n * [abort](#abortmessage \"abort\")\n * [displayStack](#displayStackslice \"displayStack\")\n * [callStack](#callStackslice \"callStack\")\n * [toss](#tosscondition \"toss\")\n* [Load files](#load-files \"Load Files\")\n * [loadPackage](#loadPackagedir \"loadpackage\")\n * [loadDirModules](#loadDirModulespath-excludefiles-target \"loaddirmodules\")\n\n\n\n# Introduction\n\nThe *Hoek* general purpose node utilities library is used to aid in a variety of manners. It comes with useful methods for Ararys (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more. \n\nFor example, to use Hoek to set configuration with default options:\n```javascript\nvar Hoek = require('hoek');\n\nvar default = {url : \"www.github.com\", port : \"8000\", debug : true}\n\nvar config = Hoek.applyToDefaults(default, {port : \"3000\", admin : true});\n\n// In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true }\n```\n\nUnder 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.\n\n## Object\n\nHoek provides several helpful methods for objects and arrays.\n\n### clone(obj)\n\nThis method is used to clone an object or an array. A *deep copy* is made (duplicates everything, including values that are objects). \n\n```javascript\n\nvar nestedObj = {\n w: /^something$/ig,\n x: {\n a: [1, 2, 3],\n b: 123456,\n c: new Date()\n },\n y: 'y',\n z: new Date()\n };\n\nvar copy = Hoek.clone(nestedObj);\n\ncopy.x.b = 100;\n\nconsole.log(copy.y) // results in 'y'\nconsole.log(nestedObj.x.b) // results in 123456\nconsole.log(copy.x.b) // results in 100\n```\n\n### merge(target, source, isNullOverride, isMergeArrays)\nisNullOverride, isMergeArrays default to true\n\nMerge all the properties of source into target, source wins in conflic, and by default null and undefined from source are applied\n\n\n```javascript\n\nvar target = {a: 1, b : 2}\nvar source = {a: 0, c: 5}\nvar source2 = {a: null, c: 5}\n\nvar targetArray = [1, 2, 3];\nvar sourceArray = [4, 5];\n\nvar newTarget = Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5}\nnewTarget = Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5}\nnewTarget = Hoek.merge(target, source2, false); // results in {a: null, b:2, c: 5}\n\nnewTarget = Hoek.merge(targetArray, sourceArray) // results in [1, 2, 3, 4, 5]\nnewTarget = Hoek.merge(targetArray, sourceArray, true, false) // results in [4, 5]\n\n\n\n\n```\n\n### applyToDefaults(defaults, options)\n\nApply options to a copy of the defaults\n\n```javascript\n\nvar defaults = {host: \"localhost\", port: 8000};\nvar options = {port: 8080};\n\nvar config = Hoek.applyToDefaults(defaults, options); // results in {host: \"localhost\", port: 8080};\n\n\n```\n\n### unique(array, key)\n\nRemove duplicate items from Array\n\n```javascript\n\nvar array = [1, 2, 2, 3, 3, 4, 5, 6];\n\nvar newArray = Hoek.unique(array); // results in [1,2,3,4,5,6];\n\narray = [{id: 1}, {id: 1}, {id: 2}];\n\nnewArray = Hoek.unique(array, \"id\") // results in [{id: 1}, {id: 2}]\n\n```\n\n### mapToObject(array, key)\n\nConvert an Array into an Object\n\n```javascript\n\nvar array = [1,2,3];\nvar newObject = Hoek.mapToObject(array); // results in [{\"1\": true}, {\"2\": true}, {\"3\": true}]\n\narray = [{id: 1}, {id: 2}];\nnewObject = Hoek.mapToObject(array, \"id\") // results in [{\"id\": 1}, {\"id\": 2}]\n\n```\n### intersect(array1, array2)\n\nFind the common unique items in two arrays\n\n```javascript\n\nvar array1 = [1, 2, 3];\nvar array2 = [1, 4, 5];\n\nvar newArray = Hoek.intersect(array1, array2) // results in [1]\n\n```\n\n### matchKeys(obj, keys) \n\nFind which keys are present\n\n```javascript\n\nvar obj = {a: 1, b: 2, c: 3};\nvar keys = [\"a\", \"e\"];\n\nHoek.matchKeys(obj, keys) // returns [\"a\"]\n\n```\n\n### flatten(array, target)\n\nFlatten an array\n\n```javascript\n\nvar array = [1, 2, 3];\nvar target = [4, 5]; \n\nvar flattenedArray = Hoek.flatten(array, target) // results in [4, 5, 1, 2, 3];\n\n```\n\n### removeKeys(object, keys)\n\nRemove keys\n\n```javascript\n\nvar object = {a: 1, b: 2, c: 3, d: 4};\n\nvar keys = [\"a\", \"b\"];\n\nHoek.removeKeys(object, keys) // object is now {c: 3, d: 4}\n\n```\n\n### reach(obj, chain)\n\nConverts an object key chain string to reference\n\n```javascript\n\nvar chain = 'a.b.c';\nvar obj = {a : {b : { c : 1}}};\n\nHoek.reach(obj, chain) // returns 1\n\n```\n\n### inheritAsync(self, obj, keys) \n\nInherits a selected set of methods from an object, wrapping functions in asynchronous syntax and catching errors\n\n```javascript\n\nvar targetFunc = function () { };\n\nvar proto = {\n a: function () {\n return 'a!';\n },\n b: function () {\n return 'b!';\n },\n c: function () {\n throw new Error('c!');\n }\n };\n\nvar keys = ['a', 'c'];\n\nHoek.inheritAsync(targetFunc, proto, ['a', 'c']);\n\nvar target = new targetFunc();\n\ntarget.a(function(err, result){console.log(result)} // returns 'a!' \n\ntarget.c(function(err, result){console.log(result)} // returns undefined\n\ntarget.b(function(err, result){console.log(result)} // gives error: Object [object Object] has no method 'b'\n\n```\n\n### rename(obj, from, to)\n\nRename a key of an object\n\n```javascript\n\nvar obj = {a : 1, b : 2};\n\nHoek.rename(obj, \"a\", \"c\"); // obj is now {c : 1, b : 2}\n\n```\n\n\n# Timer\n\nA Timer object. Initializing a new timer object sets the ts to the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.\n\n```javascript\n\n\nexample : \n\n\nvar timerObj = new Hoek.Timer();\nconsole.log(\"Time is now: \" + timerObj.ts)\nconsole.log(\"Elapsed time from initialization: \" + timerObj.elapsed() + 'milliseconds')\n\n```\n\n# Binary Encoding/Decoding\n\n### base64urlEncode(value)\n\nEncodes value in Base64 or URL encoding\n\n### base64urlDecode(value)\n\nDecodes data in Base64 or URL encoding.\n# Escaping Characters\n\nHoek provides convenient methods for escaping html characters. The escaped characters are as followed:\n\n```javascript\n\ninternals.htmlEscaped = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '`': '`'\n};\n\n```\n\n### escapeHtml(string)\n\n```javascript\n\nvar string = '<html> hey </html>';\nvar escapedString = Hoek.escapeHtml(string); // returns <html> hey </html>\n\n```\n\n### escapeHeaderAttribute(attribute)\n\nEscape attribute value for use in HTTP header\n\n```javascript\n\nvar a = Hoek.escapeHeaderAttribute('I said \"go w\\\\o me\"'); //returns I said \\\"go w\\\\o me\\\"\n\n\n```\n\n\n### escapeRegex(string)\n\nEscape string for Regex construction\n\n```javascript\n\nvar a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\\\/`\"(>)[<]d{}s,'); // returns 4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`\"\\(>\\)\\[<\\]d\\{\\}s\\,\n\n\n\n```\n\n# Errors\n\n### assert(message)\n\n```javascript\n\nvar a = 1, b =2;\n\nHoek.assert(a === b, 'a should equal b'); // ABORT: a should equal b\n\n```\n\n### abort(message)\n\nFirst checks if process.env.NODE_ENV === 'test', and if so, throws error message. Otherwise,\ndisplays most recent stack and then exits process.\n\n\n\n### displayStack(slice)\n\nDisplays the trace stack\n\n```javascript\n\nvar stack = Hoek.displayStack();\nconsole.log(stack) // returns something like:\n\n[ 'null (/Users/user/Desktop/hoek/test.js:4:18)',\n 'Module._compile (module.js:449:26)',\n 'Module._extensions..js (module.js:467:10)',\n 'Module.load (module.js:356:32)',\n 'Module._load (module.js:312:12)',\n 'Module.runMain (module.js:492:10)',\n 'startup.processNextTick.process._tickCallback (node.js:244:9)' ]\n\n```\n\n### callStack(slice)\n\nReturns a trace stack array.\n\n```javascript\n\nvar stack = Hoek.callStack();\nconsole.log(stack) // returns something like:\n\n[ [ '/Users/user/Desktop/hoek/test.js', 4, 18, null, false ],\n [ 'module.js', 449, 26, 'Module._compile', false ],\n [ 'module.js', 467, 10, 'Module._extensions..js', false ],\n [ 'module.js', 356, 32, 'Module.load', false ],\n [ 'module.js', 312, 12, 'Module._load', false ],\n [ 'module.js', 492, 10, 'Module.runMain', false ],\n [ 'node.js',\n 244,\n 9,\n 'startup.processNextTick.process._tickCallback',\n false ] ]\n\n\n```\n\n### toss(condition)\n\ntoss(condition /*, [message], callback */)\n\nReturn an error as first argument of a callback\n\n\n# Load Files\n\n### loadPackage(dir)\n\nLoad and parse package.json process root or given directory\n\n```javascript\n\nvar pack = Hoek.loadPackage(); // pack.name === 'hoek'\n\n```\n\n### loadDirModules(path, excludeFiles, target) \n\nLoads modules from a given path; option to exclude files (array).\n\n\n\n\n",
+ "readme": "<a href=\"https://github.com/spumko\"><img src=\"https://raw.github.com/spumko/spumko/master/images/from.png\" align=\"right\" /></a>\r\n![hoek Logo](https://raw.github.com/spumko/hoek/master/images/hoek.png)\r\n\r\nGeneral purpose node utilities\r\n\r\n[![Build Status](https://secure.travis-ci.org/spumko/hoek.png)](http://travis-ci.org/spumko/hoek)\r\n\r\n# Table of Contents\r\n\r\n* [Introduction](#introduction \"Introduction\")\r\n* [Object](#object \"Object\")\r\n * [clone](#cloneobj \"clone\")\r\n * [merge](#mergetarget-source-isnulloverride-ismergearrays \"merge\")\r\n * [applyToDefaults](#applytodefaultsdefaults-options \"applyToDefaults\")\r\n * [unique](#uniquearray-key \"unique\")\r\n * [mapToObject](#maptoobjectarray-key \"mapToObject\")\r\n * [intersect](#intersectarray1-array2 \"intersect\")\r\n * [matchKeys](#matchkeysobj-keys \"matchKeys\")\r\n * [flatten](#flattenarray-target \"flatten\")\r\n * [removeKeys](#removekeysobject-keys \"removeKeys\")\r\n * [reach](#reachobj-chain \"reach\")\r\n * [inheritAsync](#inheritasyncself-obj-keys \"inheritAsync\")\r\n * [rename](#renameobj-from-to \"rename\")\r\n* [Timer](#timer \"Timer\")\r\n* [Binary Encoding/Decoding](#binary \"Binary Encoding/Decoding\")\r\n * [base64urlEncode](#binary64urlEncodevalue \"binary64urlEncode\")\r\n * [base64urlDecode](#binary64urlDecodevalue \"binary64urlDecode\")\r\n* [Escaping Characters](#escaped \"Escaping Characters\")\r\n * [escapeHtml](#escapeHtmlstring \"escapeHtml\")\r\n * [escapeHeaderAttribute](#escapeHeaderAttributeattribute \"escapeHeaderAttribute\")\r\n * [escapeRegex](#escapeRegexstring \"escapeRegex\")\r\n* [Errors](#errors \"Errors\")\r\n * [assert](#assertmessage \"assert\")\r\n * [abort](#abortmessage \"abort\")\r\n * [displayStack](#displayStackslice \"displayStack\")\r\n * [callStack](#callStackslice \"callStack\")\r\n * [toss](#tosscondition \"toss\")\r\n* [Load files](#load-files \"Load Files\")\r\n * [loadPackage](#loadPackagedir \"loadpackage\")\r\n * [loadDirModules](#loadDirModulespath-excludefiles-target \"loaddirmodules\")\r\n\r\n\r\n\r\n# Introduction\r\n\r\nThe *Hoek* general purpose node utilities library is used to aid in a variety of manners. It comes with useful methods for Arrays (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more. \r\n\r\nFor example, to use Hoek to set configuration with default options:\r\n```javascript\r\nvar Hoek = require('hoek');\r\n\r\nvar default = {url : \"www.github.com\", port : \"8000\", debug : true}\r\n\r\nvar config = Hoek.applyToDefaults(default, {port : \"3000\", admin : true});\r\n\r\n// In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true }\r\n```\r\n\r\nUnder 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.\r\n\r\n## Object\r\n\r\nHoek provides several helpful methods for objects and arrays.\r\n\r\n### clone(obj)\r\n\r\nThis method is used to clone an object or an array. A *deep copy* is made (duplicates everything, including values that are objects). \r\n\r\n```javascript\r\n\r\nvar nestedObj = {\r\n w: /^something$/ig,\r\n x: {\r\n a: [1, 2, 3],\r\n b: 123456,\r\n c: new Date()\r\n },\r\n y: 'y',\r\n z: new Date()\r\n };\r\n\r\nvar copy = Hoek.clone(nestedObj);\r\n\r\ncopy.x.b = 100;\r\n\r\nconsole.log(copy.y) // results in 'y'\r\nconsole.log(nestedObj.x.b) // results in 123456\r\nconsole.log(copy.x.b) // results in 100\r\n```\r\n\r\n### merge(target, source, isNullOverride, isMergeArrays)\r\nisNullOverride, isMergeArrays default to true\r\n\r\nMerge all the properties of source into target, source wins in conflic, and by default null and undefined from source are applied\r\n\r\n\r\n```javascript\r\n\r\nvar target = {a: 1, b : 2}\r\nvar source = {a: 0, c: 5}\r\nvar source2 = {a: null, c: 5}\r\n\r\nvar targetArray = [1, 2, 3];\r\nvar sourceArray = [4, 5];\r\n\r\nvar newTarget = Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5}\r\nnewTarget = Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5}\r\nnewTarget = Hoek.merge(target, source2, false); // results in {a: 1, b: 2, c: 5}\r\n\r\nnewTarget = Hoek.merge(targetArray, sourceArray) // results in [1, 2, 3, 4, 5]\r\nnewTarget = Hoek.merge(targetArray, sourceArray, true, false) // results in [4, 5]\r\n\r\n\r\n\r\n\r\n```\r\n\r\n### applyToDefaults(defaults, options)\r\n\r\nApply options to a copy of the defaults\r\n\r\n```javascript\r\n\r\nvar defaults = {host: \"localhost\", port: 8000};\r\nvar options = {port: 8080};\r\n\r\nvar config = Hoek.applyToDefaults(defaults, options); // results in {host: \"localhost\", port: 8080};\r\n\r\n\r\n```\r\n\r\n### unique(array, key)\r\n\r\nRemove duplicate items from Array\r\n\r\n```javascript\r\n\r\nvar array = [1, 2, 2, 3, 3, 4, 5, 6];\r\n\r\nvar newArray = Hoek.unique(array); // results in [1,2,3,4,5,6];\r\n\r\narray = [{id: 1}, {id: 1}, {id: 2}];\r\n\r\nnewArray = Hoek.unique(array, \"id\") // results in [{id: 1}, {id: 2}]\r\n\r\n```\r\n\r\n### mapToObject(array, key)\r\n\r\nConvert an Array into an Object\r\n\r\n```javascript\r\n\r\nvar array = [1,2,3];\r\nvar newObject = Hoek.mapToObject(array); // results in [{\"1\": true}, {\"2\": true}, {\"3\": true}]\r\n\r\narray = [{id: 1}, {id: 2}];\r\nnewObject = Hoek.mapToObject(array, \"id\") // results in [{\"id\": 1}, {\"id\": 2}]\r\n\r\n```\r\n### intersect(array1, array2)\r\n\r\nFind the common unique items in two arrays\r\n\r\n```javascript\r\n\r\nvar array1 = [1, 2, 3];\r\nvar array2 = [1, 4, 5];\r\n\r\nvar newArray = Hoek.intersect(array1, array2) // results in [1]\r\n\r\n```\r\n\r\n### matchKeys(obj, keys) \r\n\r\nFind which keys are present\r\n\r\n```javascript\r\n\r\nvar obj = {a: 1, b: 2, c: 3};\r\nvar keys = [\"a\", \"e\"];\r\n\r\nHoek.matchKeys(obj, keys) // returns [\"a\"]\r\n\r\n```\r\n\r\n### flatten(array, target)\r\n\r\nFlatten an array\r\n\r\n```javascript\r\n\r\nvar array = [1, 2, 3];\r\nvar target = [4, 5]; \r\n\r\nvar flattenedArray = Hoek.flatten(array, target) // results in [4, 5, 1, 2, 3];\r\n\r\n```\r\n\r\n### removeKeys(object, keys)\r\n\r\nRemove keys\r\n\r\n```javascript\r\n\r\nvar object = {a: 1, b: 2, c: 3, d: 4};\r\n\r\nvar keys = [\"a\", \"b\"];\r\n\r\nHoek.removeKeys(object, keys) // object is now {c: 3, d: 4}\r\n\r\n```\r\n\r\n### reach(obj, chain)\r\n\r\nConverts an object key chain string to reference\r\n\r\n```javascript\r\n\r\nvar chain = 'a.b.c';\r\nvar obj = {a : {b : { c : 1}}};\r\n\r\nHoek.reach(obj, chain) // returns 1\r\n\r\n```\r\n\r\n### inheritAsync(self, obj, keys) \r\n\r\nInherits a selected set of methods from an object, wrapping functions in asynchronous syntax and catching errors\r\n\r\n```javascript\r\n\r\nvar targetFunc = function () { };\r\n\r\nvar proto = {\r\n a: function () {\r\n return 'a!';\r\n },\r\n b: function () {\r\n return 'b!';\r\n },\r\n c: function () {\r\n throw new Error('c!');\r\n }\r\n };\r\n\r\nvar keys = ['a', 'c'];\r\n\r\nHoek.inheritAsync(targetFunc, proto, ['a', 'c']);\r\n\r\nvar target = new targetFunc();\r\n\r\ntarget.a(function(err, result){console.log(result)} // returns 'a!' \r\n\r\ntarget.c(function(err, result){console.log(result)} // returns undefined\r\n\r\ntarget.b(function(err, result){console.log(result)} // gives error: Object [object Object] has no method 'b'\r\n\r\n```\r\n\r\n### rename(obj, from, to)\r\n\r\nRename a key of an object\r\n\r\n```javascript\r\n\r\nvar obj = {a : 1, b : 2};\r\n\r\nHoek.rename(obj, \"a\", \"c\"); // obj is now {c : 1, b : 2}\r\n\r\n```\r\n\r\n\r\n# Timer\r\n\r\nA Timer object. Initializing a new timer object sets the ts to the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.\r\n\r\n```javascript\r\n\r\n\r\nexample : \r\n\r\n\r\nvar timerObj = new Hoek.Timer();\r\nconsole.log(\"Time is now: \" + timerObj.ts)\r\nconsole.log(\"Elapsed time from initialization: \" + timerObj.elapsed() + 'milliseconds')\r\n\r\n```\r\n\r\n# Binary Encoding/Decoding\r\n\r\n### base64urlEncode(value)\r\n\r\nEncodes value in Base64 or URL encoding\r\n\r\n### base64urlDecode(value)\r\n\r\nDecodes data in Base64 or URL encoding.\r\n# Escaping Characters\r\n\r\nHoek provides convenient methods for escaping html characters. The escaped characters are as followed:\r\n\r\n```javascript\r\n\r\ninternals.htmlEscaped = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n '`': '`'\r\n};\r\n\r\n```\r\n\r\n### escapeHtml(string)\r\n\r\n```javascript\r\n\r\nvar string = '<html> hey </html>';\r\nvar escapedString = Hoek.escapeHtml(string); // returns <html> hey </html>\r\n\r\n```\r\n\r\n### escapeHeaderAttribute(attribute)\r\n\r\nEscape attribute value for use in HTTP header\r\n\r\n```javascript\r\n\r\nvar a = Hoek.escapeHeaderAttribute('I said \"go w\\\\o me\"'); //returns I said \\\"go w\\\\o me\\\"\r\n\r\n\r\n```\r\n\r\n\r\n### escapeRegex(string)\r\n\r\nEscape string for Regex construction\r\n\r\n```javascript\r\n\r\nvar a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\\\/`\"(>)[<]d{}s,'); // returns 4\\^f\\$s\\.4\\*5\\+\\-_\\?%\\=#\\!\\:@\\|~\\\\\\/`\"\\(>\\)\\[<\\]d\\{\\}s\\,\r\n\r\n\r\n\r\n```\r\n\r\n# Errors\r\n\r\n### assert(message)\r\n\r\n```javascript\r\n\r\nvar a = 1, b =2;\r\n\r\nHoek.assert(a === b, 'a should equal b'); // ABORT: a should equal b\r\n\r\n```\r\n\r\n### abort(message)\r\n\r\nFirst checks if process.env.NODE_ENV === 'test', and if so, throws error message. Otherwise,\r\ndisplays most recent stack and then exits process.\r\n\r\n\r\n\r\n### displayStack(slice)\r\n\r\nDisplays the trace stack\r\n\r\n```javascript\r\n\r\nvar stack = Hoek.displayStack();\r\nconsole.log(stack) // returns something like:\r\n\r\n[ 'null (/Users/user/Desktop/hoek/test.js:4:18)',\r\n 'Module._compile (module.js:449:26)',\r\n 'Module._extensions..js (module.js:467:10)',\r\n 'Module.load (module.js:356:32)',\r\n 'Module._load (module.js:312:12)',\r\n 'Module.runMain (module.js:492:10)',\r\n 'startup.processNextTick.process._tickCallback (node.js:244:9)' ]\r\n\r\n```\r\n\r\n### callStack(slice)\r\n\r\nReturns a trace stack array.\r\n\r\n```javascript\r\n\r\nvar stack = Hoek.callStack();\r\nconsole.log(stack) // returns something like:\r\n\r\n[ [ '/Users/user/Desktop/hoek/test.js', 4, 18, null, false ],\r\n [ 'module.js', 449, 26, 'Module._compile', false ],\r\n [ 'module.js', 467, 10, 'Module._extensions..js', false ],\r\n [ 'module.js', 356, 32, 'Module.load', false ],\r\n [ 'module.js', 312, 12, 'Module._load', false ],\r\n [ 'module.js', 492, 10, 'Module.runMain', false ],\r\n [ 'node.js',\r\n 244,\r\n 9,\r\n 'startup.processNextTick.process._tickCallback',\r\n false ] ]\r\n\r\n\r\n```\r\n\r\n### toss(condition)\r\n\r\ntoss(condition /*, [message], callback */)\r\n\r\nReturn an error as first argument of a callback\r\n\r\n\r\n# Load Files\r\n\r\n### loadPackage(dir)\r\n\r\nLoad and parse package.json process root or given directory\r\n\r\n```javascript\r\n\r\nvar pack = Hoek.loadPackage(); // pack.name === 'hoek'\r\n\r\n```\r\n\r\n### loadDirModules(path, excludeFiles, target) \r\n\r\nLoads modules from a given path; option to exclude files (array).\r\n\r\n\r\n\r\n\r\n",
"readmeFilename": "README.md",
- "_id": "hoek@0.7.6",
- "dist": {
- "shasum": "30c553c4bbfd8f78015fb48a8d643b966490cb32"
+ "bugs": {
+ "url": "https://github.com/spumko/hoek/issues"
},
- "_from": "hoek@0.7.x",
- "_resolved": "https://registry.npmjs.org/hoek/-/hoek-0.7.6.tgz"
+ "_id": "hoek@0.8.5",
+ "_from": "hoek@0.8.x"
}
-// Load modules\r
-\r
-var Lab = require('lab');\r
-var Hoek = require('../lib');\r
-\r
-\r
-// Declare internals\r
-\r
-var internals = {};\r
-\r
-\r
-// Test shortcuts\r
-\r
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
-\r
-\r
-describe('Hoek', function () {\r
-\r
- describe('#escapeJavaScript', function () {\r
-\r
- it('encodes / characters', function (done) {\r
-\r
- var encoded = Hoek.escapeJavaScript('<script>alert(1)</script>');\r
- expect(encoded).to.equal('\\x3cscript\\x3ealert\\x281\\x29\\x3c\\x2fscript\\x3e');\r
- done();\r
- });\r
-\r
- it('encodes \' characters', function (done) {\r
-\r
- var encoded = Hoek.escapeJavaScript('something(\'param\')');\r
- expect(encoded).to.equal('something\\x28\\x27param\\x27\\x29');\r
- done();\r
- });\r
-\r
- it('encodes large unicode characters with the correct padding', function (done) {\r
-\r
- var encoded = Hoek.escapeJavaScript(String.fromCharCode(500) + String.fromCharCode(1000));\r
- expect(encoded).to.equal('\\u0500\\u1000');\r
- done();\r
- });\r
-\r
- it('doesn\'t throw an exception when passed null', function (done) {\r
-\r
- var encoded = Hoek.escapeJavaScript(null);\r
- expect(encoded).to.equal('');\r
- done();\r
- });\r
- });\r
-\r
- describe('#escapeHtml', function () {\r
-\r
- it('encodes / characters', function (done) {\r
-\r
- var encoded = Hoek.escapeHtml('<script>alert(1)</script>');\r
- expect(encoded).to.equal('<script>alert(1)</script>');\r
- done();\r
- });\r
-\r
- it('encodes < and > as named characters', function (done) {\r
-\r
- var encoded = Hoek.escapeHtml('<script><>');\r
- expect(encoded).to.equal('<script><>');\r
- done();\r
- });\r
-\r
- it('encodes large unicode characters', function (done) {\r
-\r
- var encoded = Hoek.escapeHtml(String.fromCharCode(500) + String.fromCharCode(1000));\r
- expect(encoded).to.equal('ǴϨ');\r
- done();\r
- });\r
-\r
- it('doesn\'t throw an exception when passed null', function (done) {\r
-\r
- var encoded = Hoek.escapeHtml(null);\r
- expect(encoded).to.equal('');\r
- done();\r
- });\r
- });\r
-});\r
-\r
-\r
+// Load modules
+
+var Lab = require('lab');
+var Hoek = require('../lib');
+
+
+// Declare internals
+
+var internals = {};
+
+
+// Test shortcuts
+
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
+
+
+describe('Hoek', function () {
+
+ 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();
+ });
+ });
+});
+
+
expect(b.x.z).to.equal('string in function');\r
done();\r
});\r
+\r
+ it('should copy a buffer', function(done){\r
+ var tls = {\r
+ key: new Buffer([1,2,3,4,5]),\r
+ cert: new Buffer([1,2,3,4,5,6,10])\r
+ }\r
+\r
+ copiedTls = Hoek.clone(tls);\r
+ expect(Buffer.isBuffer(copiedTls.key)).to.equal(true);\r
+ expect(JSON.stringify(copiedTls.key)).to.equal(JSON.stringify(tls.key))\r
+ expect(Buffer.isBuffer(copiedTls.cert)).to.equal(true);\r
+ expect(JSON.stringify(copiedTls.cert)).to.equal(JSON.stringify(tls.cert))\r
+ done();\r
+ });\r
});\r
\r
describe('#merge', function () {\r
expect(fn).to.throw('my error message');\r
done();\r
});\r
+\r
+ it('should throw an Error when using assert in a test with no message', function (done) {\r
+\r
+ var fn = function () {\r
+\r
+ Hoek.assert(false);\r
+ };\r
+\r
+ expect(fn).to.throw('Unknown error');\r
+ done();\r
+ });\r
+\r
+ it('should throw an Error when using assert in a test with multipart message', function (done) {\r
+\r
+ var fn = function () {\r
+\r
+ Hoek.assert(false, 'This', 'is', 'my message');\r
+ };\r
+\r
+ expect(fn).to.throw('This is my message');\r
+ done();\r
+ });\r
+\r
+ it('should throw an Error when using assert in a test with object message', function (done) {\r
+\r
+ var fn = function () {\r
+\r
+ Hoek.assert(false, 'This', 'is', { spinal: 'tap' });\r
+ };\r
+\r
+ expect(fn).to.throw('This is {"spinal":"tap"}');\r
+ done();\r
+ });\r
+\r
+ it('should throw an Error when using assert in a test with error object message', function (done) {\r
+\r
+ var fn = function () {\r
+\r
+ Hoek.assert(false, new Error('This is spinal tap'));\r
+ };\r
+\r
+ expect(fn).to.throw('This is spinal tap');\r
+ done();\r
+ });\r
});\r
\r
describe('#loadDirModules', function () {\r
Hoek.printEvent(event);\r
});\r
});\r
+\r
+ describe('#nextTick', function () {\r
+\r
+ it('calls the provided callback on nextTick', function (done) {\r
+\r
+ var a = 0;\r
+\r
+ var inc = function (step, next) {\r
+\r
+ a += step;\r
+ next();\r
+ };\r
+\r
+ var ticked = Hoek.nextTick(inc);\r
+\r
+ ticked(5, function () {\r
+\r
+ expect(a).to.equal(6);\r
+ done();\r
+ });\r
+\r
+ expect(a).to.equal(0);\r
+ inc(1, function () {\r
+\r
+ expect(a).to.equal(1);\r
+ });\r
+ });\r
+ });\r
});\r
\r
language: node_js
node_js:
- - 0.8
+ - 0.10
-test:\r
- @./node_modules/.bin/lab\r
-test-cov: \r
- @./node_modules/.bin/lab -r threshold -t 100\r
-test-cov-html:\r
- @./node_modules/.bin/lab -r html -o coverage.html\r
-complexity:\r
- @./node_modules/.bin/cr -o complexity.md -f markdown lib\r
-\r
-.PHONY: test test-cov test-cov-html complexity\r
-\r
+test:
+ @./node_modules/.bin/lab
+test-cov:
+ @./node_modules/.bin/lab -r threshold -t 100
+test-cov-html:
+ @./node_modules/.bin/lab -r html -o coverage.html
+complexity:
+ @./node_modules/.bin/cr -o complexity.md -f markdown lib
+
+.PHONY: test test-cov test-cov-html complexity
+
-# 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.
-
-Current version: **0.1.1**
-
-[![Build Status](https://secure.travis-ci.org/hueniverse/sntp.png)](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();
-});
-```
-
+# sntp\r
+\r
+An SNTP v4 client (RFC4330) for node. Simpy connects to the NTP or SNTP server requested and returns the server time\r
+along with the roundtrip duration and clock offset. To adjust the local time to the NTP time, add the returned `t` offset\r
+to the local time.\r
+\r
+[![Build Status](https://secure.travis-ci.org/hueniverse/sntp.png)](http://travis-ci.org/hueniverse/sntp)\r
+\r
+# Usage\r
+\r
+```javascript\r
+var Sntp = require('sntp');\r
+\r
+// All options are optional\r
+\r
+var options = {\r
+ host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org\r
+ port: 123, // Defaults to 123 (NTP)\r
+ resolveReference: true, // Default to false (not resolving)\r
+ timeout: 1000 // Defaults to zero (no timeout)\r
+};\r
+\r
+// Request server time\r
+\r
+Sntp.time(options, function (err, time) {\r
+\r
+ if (err) {\r
+ console.log('Failed: ' + err.message);\r
+ process.exit(1);\r
+ }\r
+\r
+ console.log('Local clock is off by: ' + time.t + ' milliseconds');\r
+ process.exit(0);\r
+});\r
+```\r
+\r
+If an application needs to maintain continuous time synchronization, the module provides a stateful method for\r
+querying the current offset only when the last one is too old (defaults to daily).\r
+\r
+```javascript\r
+// Request offset once\r
+\r
+Sntp.offset(function (err, offset) {\r
+\r
+ console.log(offset); // New (served fresh)\r
+\r
+ // Request offset again\r
+\r
+ Sntp.offset(function (err, offset) {\r
+\r
+ console.log(offset); // Identical (served from cache)\r
+ });\r
+});\r
+```\r
+\r
+To set a background offset refresh, start the interval and use the provided now() method. If for any reason the\r
+client fails to obtain an up-to-date offset, the current system clock is used.\r
+\r
+```javascript\r
+var before = Sntp.now(); // System time without offset\r
+\r
+Sntp.start(function () {\r
+\r
+ var now = Sntp.now(); // With offset\r
+ Sntp.stop();\r
+});\r
+```\r
+\r
-// 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 isFinished = false;
- var finish = function (err, result) {
-
- if (timeoutId) {
- clearTimeout(timeoutId);
- timeoutId = 0;
- }
-
- if (!isFinished) {
- isFinished = true;
- socket.close();
- return callback(err, result);
- }
- };
-
- // Create UDP socket
-
- var socket = Dgram.createSocket('udp4');
-
- socket.on('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 (!err) {
- 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) {
-
- return callback(null, internals.last.offset);
- }
-
- 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) {
- return callback();
- }
-
- 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;
-};
-
+// Load modules\r
+\r
+var Dgram = require('dgram');\r
+var Dns = require('dns');\r
+var Hoek = require('hoek');\r
+\r
+\r
+// Declare internals\r
+\r
+var internals = {};\r
+\r
+\r
+exports.time = function (options, callback) {\r
+\r
+ if (arguments.length !== 2) {\r
+ callback = arguments[0];\r
+ options = {};\r
+ }\r
+\r
+ var settings = Hoek.clone(options);\r
+ settings.host = settings.host || 'pool.ntp.org';\r
+ settings.port = settings.port || 123;\r
+ settings.resolveReference = settings.resolveReference || false;\r
+\r
+ // Declare variables used by callback\r
+\r
+ var timeoutId = 0;\r
+ var sent = 0;\r
+\r
+ // Ensure callback is only called once\r
+\r
+ var isFinished = false;\r
+ var finish = function (err, result) {\r
+\r
+ if (timeoutId) {\r
+ clearTimeout(timeoutId);\r
+ timeoutId = 0;\r
+ }\r
+\r
+ if (!isFinished) {\r
+ isFinished = true;\r
+ socket.close();\r
+ return callback(err, result);\r
+ }\r
+ };\r
+\r
+ // Create UDP socket\r
+\r
+ var socket = Dgram.createSocket('udp4');\r
+\r
+ socket.on('error', function (err) {\r
+\r
+ return finish(err);\r
+ });\r
+\r
+ // Listen to incoming messages\r
+\r
+ socket.on('message', function (buffer, rinfo) {\r
+\r
+ var received = Date.now();\r
+\r
+ var message = new internals.NtpMessage(buffer);\r
+ if (!message.isValid) {\r
+ return finish(new Error('Invalid server response'), message);\r
+ }\r
+\r
+ if (message.originateTimestamp !== sent) {\r
+ return finish(new Error('Wrong originate timestamp'), message);\r
+ }\r
+\r
+ // Timestamp Name ID When Generated\r
+ // ------------------------------------------------------------\r
+ // Originate Timestamp T1 time request sent by client\r
+ // Receive Timestamp T2 time request received by server\r
+ // Transmit Timestamp T3 time reply sent by server\r
+ // Destination Timestamp T4 time reply received by client\r
+ //\r
+ // The roundtrip delay d and system clock offset t are defined as:\r
+ //\r
+ // d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2\r
+\r
+ var T1 = message.originateTimestamp;\r
+ var T2 = message.receiveTimestamp;\r
+ var T3 = message.transmitTimestamp;\r
+ var T4 = received;\r
+\r
+ message.d = (T4 - T1) - (T3 - T2);\r
+ message.t = ((T2 - T1) + (T3 - T4)) / 2;\r
+ message.receivedLocally = received;\r
+\r
+ if (!settings.resolveReference ||\r
+ message.stratum !== 'secondary') {\r
+\r
+ return finish(null, message);\r
+ }\r
+\r
+ // Resolve reference IP address\r
+\r
+ Dns.reverse(message.referenceId, function (err, domains) {\r
+\r
+ if (!err) {\r
+ message.referenceHost = domains[0];\r
+ }\r
+\r
+ return finish(null, message);\r
+ });\r
+ });\r
+\r
+ // Set timeout\r
+\r
+ if (settings.timeout) {\r
+ timeoutId = setTimeout(function () {\r
+\r
+ timeoutId = 0;\r
+ return finish(new Error('Timeout'));\r
+ }, settings.timeout);\r
+ }\r
+\r
+ // Construct NTP message\r
+\r
+ var message = new Buffer(48);\r
+ for (var i = 0; i < 48; i++) { // Zero message\r
+ message[i] = 0;\r
+ }\r
+\r
+ message[0] = (0 << 6) + (4 << 3) + (3 << 0) // Set version number to 4 and Mode to 3 (client)\r
+ sent = Date.now();\r
+ internals.fromMsecs(sent, message, 40); // Set transmit timestamp (returns as originate)\r
+\r
+ // Send NTP request\r
+\r
+ socket.send(message, 0, message.length, settings.port, settings.host, function (err, bytes) {\r
+\r
+ if (err ||\r
+ bytes !== 48) {\r
+\r
+ return finish(err || new Error('Could not send entire message'));\r
+ }\r
+ });\r
+};\r
+\r
+\r
+internals.NtpMessage = function (buffer) {\r
+\r
+ this.isValid = false;\r
+\r
+ // Validate\r
+\r
+ if (buffer.length !== 48) {\r
+ return;\r
+ }\r
+\r
+ // Leap indicator\r
+\r
+ var li = (buffer[0] >> 6);\r
+ switch (li) {\r
+ case 0: this.leapIndicator = 'no-warning'; break;\r
+ case 1: this.leapIndicator = 'last-minute-61'; break;\r
+ case 2: this.leapIndicator = 'last-minute-59'; break;\r
+ case 3: this.leapIndicator = 'alarm'; break;\r
+ }\r
+\r
+ // Version\r
+\r
+ var vn = ((buffer[0] & 0x38) >> 3);\r
+ this.version = vn;\r
+\r
+ // Mode\r
+\r
+ var mode = (buffer[0] & 0x7);\r
+ switch (mode) {\r
+ case 1: this.mode = 'symmetric-active'; break;\r
+ case 2: this.mode = 'symmetric-passive'; break;\r
+ case 3: this.mode = 'client'; break;\r
+ case 4: this.mode = 'server'; break;\r
+ case 5: this.mode = 'broadcast'; break;\r
+ case 0:\r
+ case 6:\r
+ case 7: this.mode = 'reserved'; break;\r
+ }\r
+\r
+ // Stratum\r
+\r
+ var stratum = buffer[1];\r
+ if (stratum === 0) {\r
+ this.stratum = 'death';\r
+ }\r
+ else if (stratum === 1) {\r
+ this.stratum = 'primary';\r
+ }\r
+ else if (stratum <= 15) {\r
+ this.stratum = 'secondary';\r
+ }\r
+ else {\r
+ this.stratum = 'reserved';\r
+ }\r
+\r
+ // Poll interval (msec)\r
+\r
+ this.pollInterval = Math.round(Math.pow(2, buffer[2])) * 1000;\r
+\r
+ // Precision (msecs)\r
+\r
+ this.precision = Math.pow(2, buffer[3]) * 1000;\r
+\r
+ // Root delay (msecs)\r
+\r
+ var rootDelay = 256 * (256 * (256 * buffer[4] + buffer[5]) + buffer[6]) + buffer[7];\r
+ this.rootDelay = 1000 * (rootDelay / 0x10000);\r
+\r
+ // Root dispersion (msecs)\r
+\r
+ this.rootDispersion = ((buffer[8] << 8) + buffer[9] + ((buffer[10] << 8) + buffer[11]) / Math.pow(2, 16)) * 1000;\r
+\r
+ // Reference identifier\r
+\r
+ this.referenceId = '';\r
+ switch (this.stratum) {\r
+ case 'death':\r
+ case 'primary':\r
+ this.referenceId = String.fromCharCode(buffer[12]) + String.fromCharCode(buffer[13]) + String.fromCharCode(buffer[14]) + String.fromCharCode(buffer[15]);\r
+ break;\r
+ case 'secondary':\r
+ this.referenceId = '' + buffer[12] + '.' + buffer[13] + '.' + buffer[14] + '.' + buffer[15];\r
+ break;\r
+ }\r
+\r
+ // Reference timestamp\r
+\r
+ this.referenceTimestamp = internals.toMsecs(buffer, 16);\r
+\r
+ // Originate timestamp\r
+\r
+ this.originateTimestamp = internals.toMsecs(buffer, 24);\r
+\r
+ // Receive timestamp\r
+\r
+ this.receiveTimestamp = internals.toMsecs(buffer, 32);\r
+\r
+ // Transmit timestamp\r
+\r
+ this.transmitTimestamp = internals.toMsecs(buffer, 40);\r
+\r
+ // Validate\r
+\r
+ if (this.version === 4 &&\r
+ this.stratum !== 'reserved' &&\r
+ this.mode === 'server' &&\r
+ this.originateTimestamp &&\r
+ this.receiveTimestamp &&\r
+ this.transmitTimestamp) {\r
+\r
+ this.isValid = true;\r
+ }\r
+\r
+ return this;\r
+};\r
+\r
+\r
+internals.toMsecs = function (buffer, offset) {\r
+\r
+ var seconds = 0;\r
+ var fraction = 0;\r
+\r
+ for (var i = 0; i < 4; ++i) {\r
+ seconds = (seconds * 256) + buffer[offset + i];\r
+ }\r
+\r
+ for (i = 4; i < 8; ++i) {\r
+ fraction = (fraction * 256) + buffer[offset + i];\r
+ }\r
+\r
+ return ((seconds - 2208988800 + (fraction / Math.pow(2, 32))) * 1000);\r
+};\r
+\r
+\r
+internals.fromMsecs = function (ts, buffer, offset) {\r
+\r
+ var seconds = Math.floor(ts / 1000) + 2208988800;\r
+ var fraction = Math.round((ts % 1000) / 1000 * Math.pow(2, 32));\r
+\r
+ buffer[offset + 0] = (seconds & 0xFF000000) >> 24;\r
+ buffer[offset + 1] = (seconds & 0x00FF0000) >> 16;\r
+ buffer[offset + 2] = (seconds & 0x0000FF00) >> 8;\r
+ buffer[offset + 3] = (seconds & 0x000000FF);\r
+\r
+ buffer[offset + 4] = (fraction & 0xFF000000) >> 24;\r
+ buffer[offset + 5] = (fraction & 0x00FF0000) >> 16;\r
+ buffer[offset + 6] = (fraction & 0x0000FF00) >> 8;\r
+ buffer[offset + 7] = (fraction & 0x000000FF);\r
+};\r
+\r
+\r
+// Offset singleton\r
+\r
+internals.last = {\r
+ offset: 0,\r
+ expires: 0,\r
+ host: '',\r
+ port: 0\r
+};\r
+\r
+\r
+exports.offset = function (options, callback) {\r
+\r
+ if (arguments.length !== 2) {\r
+ callback = arguments[0];\r
+ options = {};\r
+ }\r
+\r
+ var now = Date.now();\r
+ var clockSyncRefresh = options.clockSyncRefresh || 24 * 60 * 60 * 1000; // Daily\r
+\r
+ if (internals.last.offset &&\r
+ internals.last.host === options.host &&\r
+ internals.last.port === options.port &&\r
+ now < internals.last.expires) {\r
+\r
+ process.nextTick(function () {\r
+ \r
+ callback(null, internals.last.offset);\r
+ });\r
+\r
+ return;\r
+ }\r
+\r
+ exports.time(options, function (err, time) {\r
+\r
+ if (err) {\r
+ return callback(err, 0);\r
+ }\r
+\r
+ internals.last = {\r
+ offset: Math.round(time.t),\r
+ expires: now + clockSyncRefresh,\r
+ host: options.host,\r
+ port: options.port\r
+ };\r
+\r
+ return callback(null, internals.last.offset);\r
+ });\r
+};\r
+\r
+\r
+// Now singleton\r
+\r
+internals.now = {\r
+ intervalId: 0\r
+};\r
+\r
+\r
+exports.start = function (options, callback) {\r
+\r
+ if (arguments.length !== 2) {\r
+ callback = arguments[0];\r
+ options = {};\r
+ }\r
+\r
+ if (internals.now.intervalId) {\r
+ process.nextTick(function () {\r
+ \r
+ callback();\r
+ });\r
+ \r
+ return;\r
+ }\r
+\r
+ exports.offset(options, function (err, offset) {\r
+\r
+ internals.now.intervalId = setInterval(function () {\r
+\r
+ exports.offset(options, function () { });\r
+ }, options.clockSyncRefresh || 24 * 60 * 60 * 1000); // Daily\r
+\r
+ return callback();\r
+ });\r
+};\r
+\r
+\r
+exports.stop = function () {\r
+\r
+ if (!internals.now.intervalId) {\r
+ return;\r
+ }\r
+\r
+ clearInterval(internals.now.intervalId);\r
+ internals.now.intervalId = 0;\r
+};\r
+\r
+\r
+exports.isLive = function () {\r
+\r
+ return !!internals.now.intervalId;\r
+};\r
+\r
+\r
+exports.now = function () {\r
+\r
+ var now = Date.now();\r
+ if (!exports.isLive() ||\r
+ now >= internals.last.expires) {\r
+\r
+ return now;\r
+ }\r
+\r
+ return now + internals.last.offset;\r
+};\r
+\r
{
"name": "sntp",
"description": "SNTP Client",
- "version": "0.1.4",
+ "version": "0.2.2",
"author": {
"name": "Eran Hammer",
"email": "eran@hueniverse.com",
"time"
],
"engines": {
- "node": "0.8.x"
+ "node": ">=0.8.0"
},
"dependencies": {
- "hoek": "0.7.x"
+ "hoek": "0.8.x"
},
"devDependencies": {
- "lab": "0.0.x",
+ "lab": "0.1.x",
"complexity-report": "0.x.x"
},
"scripts": {
"url": "http://github.com/hueniverse/sntp/raw/master/LICENSE"
}
],
- "readme": "# sntp\n\nAn SNTP v4 client (RFC4330) for node. Simpy connects to the NTP or SNTP server requested and returns the server time\nalong with the roundtrip duration and clock offset. To adjust the local time to the NTP time, add the returned `t` offset\nto the local time.\n\nCurrent version: **0.1.1**\n\n[![Build Status](https://secure.travis-ci.org/hueniverse/sntp.png)](http://travis-ci.org/hueniverse/sntp)\n\n# Usage\n\n```javascript\nvar Sntp = require('sntp');\n\n// All options are optional\n\nvar options = {\n host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org\n port: 123, // Defaults to 123 (NTP)\n resolveReference: true, // Default to false (not resolving)\n timeout: 1000 // Defaults to zero (no timeout)\n};\n\n// Request server time\n\nSntp.time(options, function (err, time) {\n\n if (err) {\n console.log('Failed: ' + err.message);\n process.exit(1);\n }\n\n console.log('Local clock is off by: ' + time.t + ' milliseconds');\n process.exit(0);\n});\n```\n\nIf an application needs to maintain continuous time synchronization, the module provides a stateful method for\nquerying the current offset only when the last one is too old (defaults to daily).\n\n```javascript\n// Request offset once\n\nSntp.offset(function (err, offset) {\n\n console.log(offset); // New (served fresh)\n\n // Request offset again\n\n Sntp.offset(function (err, offset) {\n\n console.log(offset); // Identical (served from cache)\n });\n});\n```\n\nTo set a background offset refresh, start the interval and use the provided now() method. If for any reason the\nclient fails to obtain an up-to-date offset, the current system clock is used.\n\n```javascript\nvar before = Sntp.now(); // System time without offset\n\nSntp.start(function () {\n\n var now = Sntp.now(); // With offset\n Sntp.stop();\n});\n```\n\n",
+ "readme": "# sntp\r\n\r\nAn SNTP v4 client (RFC4330) for node. Simpy connects to the NTP or SNTP server requested and returns the server time\r\nalong with the roundtrip duration and clock offset. To adjust the local time to the NTP time, add the returned `t` offset\r\nto the local time.\r\n\r\n[![Build Status](https://secure.travis-ci.org/hueniverse/sntp.png)](http://travis-ci.org/hueniverse/sntp)\r\n\r\n# Usage\r\n\r\n```javascript\r\nvar Sntp = require('sntp');\r\n\r\n// All options are optional\r\n\r\nvar options = {\r\n host: 'nist1-sj.ustiming.org', // Defaults to pool.ntp.org\r\n port: 123, // Defaults to 123 (NTP)\r\n resolveReference: true, // Default to false (not resolving)\r\n timeout: 1000 // Defaults to zero (no timeout)\r\n};\r\n\r\n// Request server time\r\n\r\nSntp.time(options, function (err, time) {\r\n\r\n if (err) {\r\n console.log('Failed: ' + err.message);\r\n process.exit(1);\r\n }\r\n\r\n console.log('Local clock is off by: ' + time.t + ' milliseconds');\r\n process.exit(0);\r\n});\r\n```\r\n\r\nIf an application needs to maintain continuous time synchronization, the module provides a stateful method for\r\nquerying the current offset only when the last one is too old (defaults to daily).\r\n\r\n```javascript\r\n// Request offset once\r\n\r\nSntp.offset(function (err, offset) {\r\n\r\n console.log(offset); // New (served fresh)\r\n\r\n // Request offset again\r\n\r\n Sntp.offset(function (err, offset) {\r\n\r\n console.log(offset); // Identical (served from cache)\r\n });\r\n});\r\n```\r\n\r\nTo set a background offset refresh, start the interval and use the provided now() method. If for any reason the\r\nclient fails to obtain an up-to-date offset, the current system clock is used.\r\n\r\n```javascript\r\nvar before = Sntp.now(); // System time without offset\r\n\r\nSntp.start(function () {\r\n\r\n var now = Sntp.now(); // With offset\r\n Sntp.stop();\r\n});\r\n```\r\n\r\n",
"readmeFilename": "README.md",
- "_id": "sntp@0.1.4",
- "dist": {
- "shasum": "e37864ac9cb8b2926b338396cb9dd8a3df49231a"
+ "bugs": {
+ "url": "https://github.com/hueniverse/sntp/issues"
},
- "_from": "sntp@0.1.x",
- "_resolved": "https://registry.npmjs.org/sntp/-/sntp-0.1.4.tgz"
+ "_id": "sntp@0.2.2",
+ "_from": "sntp@0.2.x"
}
// Load modules
var Dgram = require('dgram');
-var Lab = require('lab');\r
+var Lab = require('lab');
var Sntp = require('../lib');
// Test shortcuts
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
describe('SNTP', function () {
{
"name": "hawk",
"description": "HTTP Hawk Authentication Scheme",
- "version": "0.10.2",
+ "version": "0.13.1",
"author": {
"name": "Eran Hammer",
"email": "eran@hueniverse.com",
"hawk"
],
"engines": {
- "node": "0.8.x"
+ "node": ">=0.8.0"
},
"dependencies": {
- "hoek": "0.7.x",
- "boom": "0.3.x",
- "cryptiles": "0.1.x",
- "sntp": "0.1.x"
+ "hoek": "0.8.x",
+ "boom": "0.4.x",
+ "cryptiles": "0.2.x",
+ "sntp": "0.2.x"
},
"devDependencies": {
- "lab": "0.0.x",
- "complexity-report": "0.x.x"
+ "lab": "0.1.x",
+ "complexity-report": "0.x.x",
+ "localStorage": "1.0.x"
},
"scripts": {
"test": "make test-cov"
"url": "http://github.com/hueniverse/hawk/raw/master/LICENSE"
}
],
- "readme": "![hawk Logo](https://raw.github.com/hueniverse/hawk/master/images/hawk.png)\r\n\r\n<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\r\nHTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](https://github.com/hueniverse/oz).\r\n\r\nCurrent version: **0.10.1**\r\n\r\n[![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk)\r\n\r\n# Table of Content\r\n\r\n- [**Introduction**](#introduction)\r\n - [Replay Protection](#replay-protection)\r\n - [Usage Example](#usage-example)\r\n - [Protocol Example](#protocol-example)\r\n - [Payload Validation](#payload-validation)\r\n - [Response Payload Validation](#response-payload-validation)\r\n<p></p>\r\n- [**Single URI Authorization**](#single-uri-authorization)\r\n - [Usage Example](#bewit-usage-example)\r\n<p></p>\r\n- [**Security Considerations**](#security-considerations)\r\n - [MAC Keys Transmission](#mac-keys-transmission)\r\n - [Confidentiality of Requests](#confidentiality-of-requests)\r\n - [Spoofing by Counterfeit Servers](#spoofing-by-counterfeit-servers)\r\n - [Plaintext Storage of Credentials](#plaintext-storage-of-credentials)\r\n - [Entropy of Keys](#entropy-of-keys)\r\n - [Coverage Limitations](#coverage-limitations)\r\n - [Future Time Manipulation](#future-time-manipulation)\r\n - [Client Clock Poisoning](#client-clock-poisoning)\r\n - [Bewit Limitations](#bewit-limitations)\r\n<p></p>\r\n- [**Frequently Asked Questions**](#frequently-asked-questions)\r\n<p></p>\r\n- [**Acknowledgements**](#acknowledgements)\r\n\r\n# Introduction\r\n\r\n**Hawk** is an HTTP authentication scheme providing mechanisms for making authenticated HTTP requests with\r\npartial cryptographic verification of the request and response, covering the HTTP method, request URI, host,\r\nand optionally the request payload.\r\n\r\nSimilar to the HTTP [Digest access authentication schemes](http://www.ietf.org/rfc/rfc2617.txt), **Hawk** uses a set of\r\nclient credentials which include an identifier (e.g. username) and key (e.g. password). Likewise, just as with the Digest scheme,\r\nthe key is never included in authenticated requests. Instead, it is used to calculate a request MAC value which is\r\nincluded in its place.\r\n\r\nHowever, **Hawk** has several differences from Digest. In particular, while both use a nonce to limit the possibility of\r\nreplay attacks, in **Hawk** the client generates the nonce and uses it in combination with a timestamp, leading to less\r\n\"chattiness\" (interaction with the server).\r\n\r\nAlso unlike Digest, this scheme is not intended to protect the key itself (the password in Digest) because\r\nthe client and server must both have access to the key material in the clear.\r\n\r\nThe primary design goals of this scheme are to:\r\n* simplify and improve HTTP authentication for services that are unwilling or unable to deploy TLS for all resources,\r\n* secure credentials against leakage (e.g., when the client uses some form of dynamic configuration to determine where\r\n to send an authenticated request), and\r\n* avoid the exposure of credentials sent to a malicious server over an unauthenticated secure channel due to client\r\n failure to validate the server's identity as part of its TLS handshake.\r\n\r\nIn addition, **Hawk** supports a method for granting third-parties temporary access to individual resources using\r\na query parameter called _bewit_ (in falconry, a leather strap used to attach a tracking device to the leg of a hawk).\r\n\r\nThe **Hawk** scheme requires the establishment of a shared symmetric key between the client and the server,\r\nwhich is beyond the scope of this module. Typically, the shared credentials are established via an initial\r\nTLS-protected phase or derived from some other shared confidential information available to both the client\r\nand the server.\r\n\r\n\r\n## Replay Protection\r\n\r\nWithout replay protection, an attacker can use a compromised (but otherwise valid and authenticated) request more \r\nthan once, gaining access to a protected resource. To mitigate this, clients include both a nonce and a timestamp when \r\nmaking requests. This gives the server enough information to prevent replay attacks.\r\n\r\nThe nonce is generated by the client, and is a string unique across all requests with the same timestamp and\r\nkey identifier combination. \r\n\r\nThe timestamp enables the server to restrict the validity period of the credentials where requests occuring afterwards\r\nare rejected. It also removes the need for the server to retain an unbounded number of nonce values for future checks.\r\nBy default, **Hawk** uses a time window of 1 minute to allow for time skew between the client and server (which in\r\npractice translates to a maximum of 2 minutes as the skew can be positive or negative).\r\n\r\nUsing a timestamp requires the client's clock to be in sync with the server's clock. **Hawk** requires both the client\r\nclock and the server clock to use NTP to ensure synchronization. However, given the limitations of some client types\r\n(e.g. browsers) to deploy NTP, the server provides the client with its current time in response to a bad timestamp.\r\n\r\nThere is no expectation that the client will adjust its system clock to match the server (in fact, this would be a\r\npotential attack vector). Instead, the client only uses the server's time to calculate an offset used only\r\nfor communications with that particular server. The protocol rewards clients with synchronized clocks by reducing\r\nthe number of round trips required to authenticate the first request.\r\n\r\n\r\n## Usage Example\r\n\r\nServer code:\r\n\r\n```javascript\r\nvar Http = require('http');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Credentials lookup function\r\n\r\nvar credentialsFunc = function (id, callback) {\r\n\r\n var credentials = {\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256',\r\n user: 'Steve'\r\n };\r\n\r\n return callback(null, credentials);\r\n};\r\n\r\n// Create HTTP server\r\n\r\nvar handler = function (req, res) {\r\n\r\n // Authenticate incoming request\r\n\r\n Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r\n\r\n // Prepare response\r\n\r\n var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');\r\n var headers = { 'Content-Type': 'text/plain' };\r\n\r\n // Generate Server-Authorization response header\r\n\r\n var header = Hawk.server.header(artifacts, { payload: payload, contentType: headers['Content-Type'] });\r\n headers['Server-Authorization'] = header;\r\n\r\n // Send the response back\r\n\r\n res.writeHead(!err ? 200 : 401, headers);\r\n res.end(payload);\r\n });\r\n};\r\n\r\n// Start server\r\n\r\nHttp.createServer(handler).listen(8000, 'example.com');\r\n```\r\n\r\nClient code:\r\n\r\n```javascript\r\nvar Request = require('request');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Client credentials\r\n\r\nvar credentials = {\r\n id: 'dh37fgj492je',\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256'\r\n}\r\n\r\n// Request options\r\n\r\nvar requestOptions = {\r\n uri: 'http://example.com:8000/resource/1?b=1&a=2',\r\n method: 'GET',\r\n headers: {}\r\n};\r\n\r\n// Generate Authorization request header\r\n\r\nvar header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });\r\nrequestOptions.headers.Authorization = header.field;\r\n\r\n// Send authenticated request\r\n\r\nRequest(requestOptions, function (error, response, body) {\r\n\r\n // Authenticate the server's response\r\n\r\n var isValid = Hawk.client.authenticate(response, header.artifacts, { payload: body });\r\n\r\n // Output results\r\n\r\n console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));\r\n});\r\n```\r\n\r\n**Hawk** utilized the [**SNTP**](https://github.com/hueniverse/sntp) module for time sync management. By default, the local\r\nmachine time is used. To automatically retrieve and synchronice the clock within the application, use the SNTP 'start()' method.\r\n\r\n```javascript\r\nHawk.sntp.start();\r\n```\r\n\r\n\r\n## Protocol Example\r\n\r\nThe client attempts to access a protected resource without authentication, sending the following HTTP request to\r\nthe resource server:\r\n\r\n```\r\nGET /resource/1?b=1&a=2 HTTP/1.1\r\nHost: example.com:8000\r\n```\r\n\r\nThe resource server returns an authentication challenge.\r\n\r\n```\r\nHTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Hawk\r\n```\r\n\r\nThe client has previously obtained a set of **Hawk** credentials for accessing resources on the \"http://example.com/\"\r\nserver. The **Hawk** credentials issued to the client include the following attributes:\r\n\r\n* Key identifier: dh37fgj492je\r\n* Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn\r\n* Algorithm: sha256\r\n\r\nThe client generates the authentication header by calculating a timestamp (e.g. the number of seconds since January 1,\r\n1970 00:00:00 GMT), generating a nonce, and constructing the normalized request string (each value followed by a newline\r\ncharacter):\r\n\r\n```\r\nhawk.1.header\r\n1353832234\r\nj4h3g2\r\nGET\r\n/resource?a=1&b=2\r\nexample.com\r\n8000\r\n\r\nsome-app-ext-data\r\n\r\n```\r\n\r\nThe request MAC is calculated using HMAC with the specified hash algorithm \"sha256\" and the key over the normalized request string.\r\nThe result is base64-encoded to produce the request MAC:\r\n\r\n```\r\n6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\r\n```\r\n\r\nThe client includes the **Hawk** key identifier, timestamp, nonce, application specific data, and request MAC with the request using\r\nthe HTTP `Authorization` request header field:\r\n\r\n```\r\nGET /resource/1?b=1&a=2 HTTP/1.1\r\nHost: example.com:8000\r\nAuthorization: Hawk id=\"dh37fgj492je\", ts=\"1353832234\", nonce=\"j4h3g2\", ext=\"some-app-ext-data\", mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\"\r\n```\r\n\r\nThe server validates the request by calculating the request MAC again based on the request received and verifies the validity\r\nand scope of the **Hawk** credentials. If valid, the server responds with the requested resource.\r\n\r\n\r\n### Payload Validation\r\n\r\n**Hawk** provides optional payload validation. When generating the authentication header, the client calculates a payload hash\r\nusing the specified hash algorithm. The hash is calculated over the concatenated value of (each followed by a newline character):\r\n* `hawk.1.payload`\r\n* the content-type in lowercase, without any parameters (e.g. `application/json`)\r\n* 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)\r\n\r\nFor example:\r\n\r\n* Payload: `Thank you for flying Hawk`\r\n* Content Type: `text/plain`\r\n* Hash (sha256): `Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=`\r\n\r\nResults in the following input to the payload hash function (newline terminated values):\r\n\r\n```\r\nhawk.1.payload\r\ntext/plain\r\nThank you for flying Hawk\r\n\r\n```\r\n\r\nWhich produces the following hash value:\r\n\r\n```\r\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\r\n```\r\n\r\nThe client constructs the normalized request string (newline terminated values):\r\n\r\n```\r\nhawk.1.header\r\n1353832234\r\nj4h3g2\r\nPOST\r\n/resource?a=1&b=2\r\nexample.com\r\n8000\r\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\r\nsome-app-ext-data\r\n\r\n```\r\n\r\nThen calculates the request MAC and includes the **Hawk** key identifier, timestamp, nonce, payload hash, application specific data,\r\nand request MAC, with the request using the HTTP `Authorization` request header field:\r\n\r\n```\r\nPOST /resource/1 HTTP/1.1\r\nHost: example.com:8000\r\nAuthorization: Hawk id=\"dh37fgj492je\", ts=\"1353832234\", nonce=\"j4h3g2\", hash=\"Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\", ext=\"some-app-ext-data\", mac=\"aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw=\"\r\n```\r\n\r\nIt is up to the server if and when it validates the payload for any given request, based solely on it's security policy\r\nand the nature of the data included.\r\n\r\nIf the payload is available at the time of authentication, the server uses the hash value provided by the client to construct\r\nthe normalized string and validates the MAC. If the MAC is valid, the server calculates the payload hash and compares the value\r\nwith the provided payload hash in the header. In many cases, checking the MAC first is faster than calculating the payload hash.\r\n\r\nHowever, if the payload is not available at authentication time (e.g. too large to fit in memory, streamed elsewhere, or processed\r\nat a different stage in the application), the server may choose to defer payload validation for later by retaining the hash value\r\nprovided by the client after validating the MAC.\r\n\r\nIt is important to note that MAC validation does not mean the hash value provided by the client is valid, only that the value\r\nincluded in the header was not modified. Without calculating the payload hash on the server and comparing it to the value provided\r\nby the client, the payload may be modified by an attacker.\r\n\r\n\r\n## Response Payload Validation\r\n\r\n**Hawk** provides partial response payload validation. The server includes the `Server-Authorization` response header which enables the\r\nclient to authenticate the response and ensure it is talking to the right server. **Hawk** defines the HTTP `Server-Authorization` header\r\nas a response header using the exact same syntax as the `Authorization` request header field.\r\n\r\nThe header is contructed using the same process as the client's request header. The server uses the same credentials and other\r\nartifacts provided by the client to constructs the normalized request string. The `ext` and `hash` values are replaced with\r\nnew values based on the server response. The rest as identical to those used by the client.\r\n\r\nThe result MAC digest is included with the optional `hash` and `ext` values:\r\n\r\n```\r\nServer-Authorization: Hawk mac=\"XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\", ext=\"response-specific\"\r\n```\r\n\r\n\r\n# Single URI Authorization\r\n\r\nThere are cases in which limited and short-term access to a protected resource is granted to a third party which does not\r\nhave access to the shared credentials. For example, displaying a protected image on a web page accessed by anyone. **Hawk**\r\nprovides limited support for such URIs in the form of a _bewit_ - a URI query parameter appended to the request URI which contains\r\nthe necessary credentials to authenticate the request.\r\n\r\nBecause of the significant security risks involved in issuing such access, bewit usage is purposely limited only to GET requests\r\nand for a finite period of time. Both the client and server can issue bewit credentials, however, the server should not use the same\r\ncredentials as the client to maintain clear traceability as to who issued which credentials.\r\n\r\nIn order to simplify implementation, bewit credentials do not support single-use policy and can be replayed multiple times within\r\nthe granted access timeframe. \r\n\r\n\r\n## Bewit Usage Example\r\n\r\nServer code:\r\n\r\n```javascript\r\nvar Http = require('http');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Credentials lookup function\r\n\r\nvar credentialsFunc = function (id, callback) {\r\n\r\n var credentials = {\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256'\r\n };\r\n\r\n return callback(null, credentials);\r\n};\r\n\r\n// Create HTTP server\r\n\r\nvar handler = function (req, res) {\r\n\r\n Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {\r\n\r\n res.writeHead(!err ? 200 : 401, { 'Content-Type': 'text/plain' });\r\n res.end(!err ? 'Access granted' : 'Shoosh!');\r\n });\r\n};\r\n\r\nHttp.createServer(handler).listen(8000, 'example.com');\r\n```\r\n\r\nBewit code generation:\r\n\r\n```javascript\r\nvar Request = require('request');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Client credentials\r\n\r\nvar credentials = {\r\n id: 'dh37fgj492je',\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256'\r\n}\r\n\r\n// Generate bewit\r\n\r\nvar duration = 60 * 5; // 5 Minutes\r\nvar bewit = Hawk.uri.getBewit('http://example.com:8080/resource/1?b=1&a=2', { credentials: credentials, ttlSec: duration, ext: 'some-app-data' });\r\nvar uri = 'http://example.com:8000/resource/1?b=1&a=2' + '&bewit=' + bewit;\r\n```\r\n\r\n\r\n# Security Considerations\r\n\r\nThe greatest sources of security risks are usually found not in **Hawk** but in the policies and procedures surrounding its use.\r\nImplementers are strongly encouraged to assess how this module addresses their security requirements. This section includes\r\nan incomplete list of security considerations that must be reviewed and understood before deploying **Hawk** on the server.\r\nMany of the protections provided in **Hawk** depends on whether and how they are used.\r\n\r\n### MAC Keys Transmission\r\n\r\n**Hawk** does not provide any mechanism for obtaining or transmitting the set of shared credentials required. Any mechanism used\r\nto obtain **Hawk** credentials must ensure that these transmissions are protected using transport-layer mechanisms such as TLS.\r\n\r\n### Confidentiality of Requests\r\n\r\nWhile **Hawk** provides a mechanism for verifying the integrity of HTTP requests, it provides no guarantee of request\r\nconfidentiality. Unless other precautions are taken, eavesdroppers will have full access to the request content. Servers should\r\ncarefully consider the types of data likely to be sent as part of such requests, and employ transport-layer security mechanisms\r\nto protect sensitive resources.\r\n\r\n### Spoofing by Counterfeit Servers\r\n\r\n**Hawk** provides limited verification of the server authenticity. When receiving a response back from the server, the server\r\nmay choose to include a response `Server-Authorization` header which the client can use to verify the response. However, it is up to\r\nthe server to determine when such measure is included, to up to the client to enforce that policy.\r\n\r\nA hostile party could take advantage of this by intercepting the client's requests and returning misleading or otherwise\r\nincorrect responses. Service providers should consider such attacks when developing services using this protocol, and should\r\nrequire transport-layer security for any requests where the authenticity of the resource server or of server responses is an issue.\r\n\r\n### Plaintext Storage of Credentials\r\n\r\nThe **Hawk** key functions the same way passwords do in traditional authentication systems. In order to compute the request MAC,\r\nthe server must have access to the key in plaintext form. This is in contrast, for example, to modern operating systems, which\r\nstore only a one-way hash of user credentials.\r\n\r\nIf 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\r\nto perform any action on behalf of any resource owner. Accordingly, it is critical that servers protect these keys from unauthorized\r\naccess.\r\n\r\n### Entropy of Keys\r\n\r\nUnless a transport-layer security protocol is used, eavesdroppers will have full access to authenticated requests and request\r\nMAC values, and will thus be able to mount offline brute-force attacks to recover the key used. Servers should be careful to\r\nassign keys which are long enough, and random enough, to resist such attacks for at least the length of time that the **Hawk**\r\ncredentials are valid.\r\n\r\nFor example, if the credentials are valid for two weeks, servers should ensure that it is not possible to mount a brute force\r\nattack that recovers the key in less than two weeks. Of course, servers are urged to err on the side of caution, and use the\r\nlongest key reasonable.\r\n\r\nIt is equally important that the pseudo-random number generator (PRNG) used to generate these keys be of sufficiently high\r\nquality. Many PRNG implementations generate number sequences that may appear to be random, but which nevertheless exhibit\r\npatterns or other weaknesses which make cryptanalysis or brute force attacks easier. Implementers should be careful to use\r\ncryptographically secure PRNGs to avoid these problems.\r\n\r\n### Coverage Limitations\r\n\r\nThe request MAC only covers the HTTP `Host` header and optionally the `Content-Type` header. It does not cover any other headers\r\nwhich can often affect how the request body is interpreted by the server. If the server behavior is influenced by the presence\r\nor value of such headers, an attacker can manipulate the request headers without being detected. Implementers should use the\r\n`ext` feature to pass application-specific information via the `Authorization` header which is protected by the request MAC.\r\n\r\nThe response authentication, when performed, only covers the response payload, content-type, and the request information \r\nprovided by the client in it's request (method, resource, timestamp, nonce, etc.). It does not cover the HTTP status code or\r\nany other response header field (e.g. Location) which can affect the client's behaviour.\r\n\r\n### Future Time Manipulation\r\n\r\nThe protocol relies on a clock sync between the client and server. To accomplish this, the server informs the client of its\r\ncurrent time when an invalid timestamp is received.\r\n\r\nIf an attacker is able to manipulate this information and cause the client to use an incorrect time, it would be able to cause\r\nthe client to generate authenticated requests using time in the future. Such requests will fail when sent by the client, and will\r\nnot likely leave a trace on the server (given the common implementation of nonce, if at all enforced). The attacker will then\r\nbe able to replay the request at the correct time without detection.\r\n\r\nThe client must only use the time information provided by the server if:\r\n* it was delivered over a TLS connection and the server identity has been verified, or\r\n* the `tsm` MAC digest calculated using the same client credentials over the timestamp has been verified.\r\n\r\n### Client Clock Poisoning\r\n\r\nWhen receiving a request with a bad timestamp, the server provides the client with its current time. The client must never use\r\nthe time received from the server to adjust its own clock, and must only use it to calculate an offset for communicating with\r\nthat particular server.\r\n\r\n### Bewit Limitations\r\n\r\nSpecial care must be taken when issuing bewit credentials to third parties. Bewit credentials are valid until expiration and cannot\r\nbe revoked or limited without using other means. Whatever resource they grant access to will be completely exposed to anyone with\r\naccess to the bewit credentials which act as bearer credentials for that particular resource. While bewit usage is limited to GET\r\nrequests only and therefore cannot be used to perform transactions or change server state, it can still be used to expose private\r\nand sensitive information.\r\n\r\n\r\n# Frequently Asked Questions\r\n\r\n### Where is the protocol specification?\r\n\r\nIf you are looking for some prose explaining how all this works, **this is it**. **Hawk** is being developed as an open source\r\nproject instead of a standard. In other words, the [code](/hueniverse/hawk/tree/master/lib) is the specification. Not sure about\r\nsomething? Open an issue!\r\n\r\n### Is it done?\r\n\r\nAt if version 0.10.0, **Hawk** is feature-complete. However, until this module reaches version 1.0.0 it is considered experimental\r\nand is likely to change. This also means your feedback and contribution are very welcome. Feel free to open issues with questions\r\nand suggestions.\r\n\r\n### Where can I find **Hawk** implementations in other languages?\r\n\r\n**Hawk**'s only reference implementation is provided in JavaScript as a node.js module. However, others are actively porting it to other\r\nplatforms. There is already a [PHP](https://github.com/alexbilbie/PHP-Hawk),\r\n[.NET](https://github.com/pcibraro/hawknet), and [JAVA](https://github.com/wealdtech/hawk) libraries available. The full list\r\nis maintained [here](https://github.com/hueniverse/hawk/issues?labels=port). Please add an issue if you are working on another\r\nport. A cross-platform test-suite is in the works.\r\n\r\n### Why isn't the algorithm part of the challenge or dynamically negotiated?\r\n\r\nThe algorithm used is closely related to the key issued as different algorithms require different key sizes (and other\r\nrequirements). While some keys can be used for multiple algorithm, the protocol is designed to closely bind the key and algorithm\r\ntogether as part of the issued credentials.\r\n\r\n### Why is Host and Content-Type the only headers covered by the request MAC?\r\n\r\nIt is really hard to include other headers. Headers can be changed by proxies and other intermediaries and there is no\r\nwell-established way to normalize them. Many platforms change the case of header field names and values. The only\r\nstraight-forward solution is to include the headers in some blob (say, base64 encoded JSON) and include that with the request,\r\nan approach taken by JWT and other such formats. However, that design violates the HTTP header boundaries, repeats information,\r\nand introduces other security issues because firewalls will not be aware of these \"hidden\" headers. In addition, any information\r\nrepeated must be compared to the duplicated information in the header and therefore only moves the problem elsewhere.\r\n\r\n### Why not just use HTTP Digest?\r\n\r\nDigest requires pre-negotiation to establish a nonce. This means you can't just make a request - you must first send\r\na protocol handshake to the server. This pattern has become unacceptable for most web services, especially mobile\r\nwhere extra round-trip are costly.\r\n\r\n### Why bother with all this nonce and timestamp business?\r\n\r\n**Hawk** is an attempt to find a reasonable, practical compromise between security and usability. OAuth 1.0 got timestamp\r\nand nonces halfway right but failed when it came to scalability and consistent developer experience. **Hawk** addresses\r\nit by requiring the client to sync its clock, but provides it with tools to accomplish it.\r\n\r\nIn general, replay protection is a matter of application-specific threat model. It is less of an issue on a TLS-protected\r\nsystem where the clients are implemented using best practices and are under the control of the server. Instead of dropping\r\nreplay protection, **Hawk** offers a required time window and an optional nonce verification. Together, it provides developers\r\nwith the ability to decide how to enforce their security policy without impacting the client's implementation.\r\n\r\n### What are `app` and `dlg` in the authorization header and normalized mac string?\r\n\r\nThe original motivation for **Hawk** was to replace the OAuth 1.0 use cases. This included both a simple client-server mode which\r\nthis module is specifically designed for, and a delegated access mode which is being developed separately in\r\n[Oz](https://github.com/hueniverse/oz). In addition to the **Hawk** use cases, Oz requires another attribute: the application id `app`.\r\nThis provides binding between the credentials and the application in a way that prevents an attacker from tricking an application\r\nto use credentials issued to someone else. It also has an optional 'delegated-by' attribute `dlg` which is the application id of the\r\napplication the credentials were directly issued to. The goal of these two additions is to allow Oz to utilize **Hawk** directly,\r\nbut with the additional security of delegated credentials.\r\n\r\n### What is the purpose of the static strings used in each normalized MAC input?\r\n\r\nWhen calculating a hash or MAC, a static prefix (tag) is added. The prefix is used to prevent MAC values from being\r\nused or reused for a purpose other than what they were created for (i.e. prevents switching MAC values between a request,\r\nresponse, and a bewit use cases). It also protects against expliots created after a potential change in how the protocol\r\ncreates the normalized string. For example, if a future version would switch the order of nonce and timestamp, it\r\ncan create an exploit opportunity for cases where the nonce is similar in format to a timestamp.\r\n\r\n### Does **Hawk** have anything to do with OAuth?\r\n\r\nShort answer: no.\r\n\r\n**Hawk** was originally proposed as the OAuth MAC Token specification. However, the OAuth working group in its consistent\r\nincompetence failed to produce a final, usable solution to address one of the most popular use cases of OAuth 1.0 - using it\r\nto authenticate simple client-server transactions (i.e. two-legged). As you can guess, the OAuth working group is still hard\r\nat work to produce more garbage.\r\n\r\n**Hawk** provides a simple HTTP authentication scheme for making client-server requests. It does not address the OAuth use case\r\nof delegating access to a third party. If you are looking for an OAuth alternative, check out [Oz](/hueniverse/oz).\r\n\r\n\r\n# Acknowledgements\r\n\r\n**Hawk** is a derivative work of the [HTTP MAC Authentication Scheme](http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05) proposal\r\nco-authored by Ben Adida, Adam Barth, and Eran Hammer, which in turn was based on the OAuth 1.0 community specification.\r\n\r\nSpecial thanks to Ben Laurie for his always insightful feedback and advice.\r\n\r\nThe **Hawk** logo was created by [Chris Carrasco](http://chriscarrasco.com).\r\n",
+ "readme": "![hawk Logo](https://raw.github.com/hueniverse/hawk/master/images/hawk.png)\r\n\r\n<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\r\nHTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](https://github.com/hueniverse/oz).\r\n\r\nCurrent version: **0.13**\r\n\r\n[![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk)\r\n\r\n# Table of Content\r\n\r\n- [**Introduction**](#introduction)\r\n - [Replay Protection](#replay-protection)\r\n - [Usage Example](#usage-example)\r\n - [Protocol Example](#protocol-example)\r\n - [Payload Validation](#payload-validation)\r\n - [Response Payload Validation](#response-payload-validation)\r\n - [Browser Support and Considerations](#browser-support-and-considerations)\r\n<p></p>\r\n- [**Single URI Authorization**](#single-uri-authorization)\r\n - [Usage Example](#bewit-usage-example)\r\n<p></p>\r\n- [**Security Considerations**](#security-considerations)\r\n - [MAC Keys Transmission](#mac-keys-transmission)\r\n - [Confidentiality of Requests](#confidentiality-of-requests)\r\n - [Spoofing by Counterfeit Servers](#spoofing-by-counterfeit-servers)\r\n - [Plaintext Storage of Credentials](#plaintext-storage-of-credentials)\r\n - [Entropy of Keys](#entropy-of-keys)\r\n - [Coverage Limitations](#coverage-limitations)\r\n - [Future Time Manipulation](#future-time-manipulation)\r\n - [Client Clock Poisoning](#client-clock-poisoning)\r\n - [Bewit Limitations](#bewit-limitations)\r\n<p></p>\r\n- [**Frequently Asked Questions**](#frequently-asked-questions)\r\n<p></p>\r\n- [**Acknowledgements**](#acknowledgements)\r\n\r\n# Introduction\r\n\r\n**Hawk** is an HTTP authentication scheme providing mechanisms for making authenticated HTTP requests with\r\npartial cryptographic verification of the request and response, covering the HTTP method, request URI, host,\r\nand optionally the request payload.\r\n\r\nSimilar to the HTTP [Digest access authentication schemes](http://www.ietf.org/rfc/rfc2617.txt), **Hawk** uses a set of\r\nclient credentials which include an identifier (e.g. username) and key (e.g. password). Likewise, just as with the Digest scheme,\r\nthe key is never included in authenticated requests. Instead, it is used to calculate a request MAC value which is\r\nincluded in its place.\r\n\r\nHowever, **Hawk** has several differences from Digest. In particular, while both use a nonce to limit the possibility of\r\nreplay attacks, in **Hawk** the client generates the nonce and uses it in combination with a timestamp, leading to less\r\n\"chattiness\" (interaction with the server).\r\n\r\nAlso unlike Digest, this scheme is not intended to protect the key itself (the password in Digest) because\r\nthe client and server must both have access to the key material in the clear.\r\n\r\nThe primary design goals of this scheme are to:\r\n* simplify and improve HTTP authentication for services that are unwilling or unable to deploy TLS for all resources,\r\n* secure credentials against leakage (e.g., when the client uses some form of dynamic configuration to determine where\r\n to send an authenticated request), and\r\n* avoid the exposure of credentials sent to a malicious server over an unauthenticated secure channel due to client\r\n failure to validate the server's identity as part of its TLS handshake.\r\n\r\nIn addition, **Hawk** supports a method for granting third-parties temporary access to individual resources using\r\na query parameter called _bewit_ (in falconry, a leather strap used to attach a tracking device to the leg of a hawk).\r\n\r\nThe **Hawk** scheme requires the establishment of a shared symmetric key between the client and the server,\r\nwhich is beyond the scope of this module. Typically, the shared credentials are established via an initial\r\nTLS-protected phase or derived from some other shared confidential information available to both the client\r\nand the server.\r\n\r\n\r\n## Replay Protection\r\n\r\nWithout replay protection, an attacker can use a compromised (but otherwise valid and authenticated) request more \r\nthan once, gaining access to a protected resource. To mitigate this, clients include both a nonce and a timestamp when \r\nmaking requests. This gives the server enough information to prevent replay attacks.\r\n\r\nThe nonce is generated by the client, and is a string unique across all requests with the same timestamp and\r\nkey identifier combination. \r\n\r\nThe timestamp enables the server to restrict the validity period of the credentials where requests occuring afterwards\r\nare rejected. It also removes the need for the server to retain an unbounded number of nonce values for future checks.\r\nBy default, **Hawk** uses a time window of 1 minute to allow for time skew between the client and server (which in\r\npractice translates to a maximum of 2 minutes as the skew can be positive or negative).\r\n\r\nUsing a timestamp requires the client's clock to be in sync with the server's clock. **Hawk** requires both the client\r\nclock and the server clock to use NTP to ensure synchronization. However, given the limitations of some client types\r\n(e.g. browsers) to deploy NTP, the server provides the client with its current time (in seconds precision) in response\r\nto a bad timestamp.\r\n\r\nThere is no expectation that the client will adjust its system clock to match the server (in fact, this would be a\r\npotential attack vector). Instead, the client only uses the server's time to calculate an offset used only\r\nfor communications with that particular server. The protocol rewards clients with synchronized clocks by reducing\r\nthe number of round trips required to authenticate the first request.\r\n\r\n\r\n## Usage Example\r\n\r\nServer code:\r\n\r\n```javascript\r\nvar Http = require('http');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Credentials lookup function\r\n\r\nvar credentialsFunc = function (id, callback) {\r\n\r\n var credentials = {\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256',\r\n user: 'Steve'\r\n };\r\n\r\n return callback(null, credentials);\r\n};\r\n\r\n// Create HTTP server\r\n\r\nvar handler = function (req, res) {\r\n\r\n // Authenticate incoming request\r\n\r\n Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r\n\r\n // Prepare response\r\n\r\n var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');\r\n var headers = { 'Content-Type': 'text/plain' };\r\n\r\n // Generate Server-Authorization response header\r\n\r\n var header = Hawk.server.header(credentials, artifacts, { payload: payload, contentType: headers['Content-Type'] });\r\n headers['Server-Authorization'] = header;\r\n\r\n // Send the response back\r\n\r\n res.writeHead(!err ? 200 : 401, headers);\r\n res.end(payload);\r\n });\r\n};\r\n\r\n// Start server\r\n\r\nHttp.createServer(handler).listen(8000, 'example.com');\r\n```\r\n\r\nClient code:\r\n\r\n```javascript\r\nvar Request = require('request');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Client credentials\r\n\r\nvar credentials = {\r\n id: 'dh37fgj492je',\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256'\r\n}\r\n\r\n// Request options\r\n\r\nvar requestOptions = {\r\n uri: 'http://example.com:8000/resource/1?b=1&a=2',\r\n method: 'GET',\r\n headers: {}\r\n};\r\n\r\n// Generate Authorization request header\r\n\r\nvar header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });\r\nrequestOptions.headers.Authorization = header.field;\r\n\r\n// Send authenticated request\r\n\r\nRequest(requestOptions, function (error, response, body) {\r\n\r\n // Authenticate the server's response\r\n\r\n var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });\r\n\r\n // Output results\r\n\r\n console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));\r\n});\r\n```\r\n\r\n**Hawk** utilized the [**SNTP**](https://github.com/hueniverse/sntp) module for time sync management. By default, the local\r\nmachine time is used. To automatically retrieve and synchronice the clock within the application, use the SNTP 'start()' method.\r\n\r\n```javascript\r\nHawk.sntp.start();\r\n```\r\n\r\n\r\n## Protocol Example\r\n\r\nThe client attempts to access a protected resource without authentication, sending the following HTTP request to\r\nthe resource server:\r\n\r\n```\r\nGET /resource/1?b=1&a=2 HTTP/1.1\r\nHost: example.com:8000\r\n```\r\n\r\nThe resource server returns an authentication challenge.\r\n\r\n```\r\nHTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Hawk\r\n```\r\n\r\nThe client has previously obtained a set of **Hawk** credentials for accessing resources on the \"http://example.com/\"\r\nserver. The **Hawk** credentials issued to the client include the following attributes:\r\n\r\n* Key identifier: dh37fgj492je\r\n* Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn\r\n* Algorithm: sha256\r\n\r\nThe client generates the authentication header by calculating a timestamp (e.g. the number of seconds since January 1,\r\n1970 00:00:00 GMT), generating a nonce, and constructing the normalized request string (each value followed by a newline\r\ncharacter):\r\n\r\n```\r\nhawk.1.header\r\n1353832234\r\nj4h3g2\r\nGET\r\n/resource?a=1&b=2\r\nexample.com\r\n8000\r\n\r\nsome-app-ext-data\r\n\r\n```\r\n\r\nThe request MAC is calculated using HMAC with the specified hash algorithm \"sha256\" and the key over the normalized request string.\r\nThe result is base64-encoded to produce the request MAC:\r\n\r\n```\r\n6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\r\n```\r\n\r\nThe client includes the **Hawk** key identifier, timestamp, nonce, application specific data, and request MAC with the request using\r\nthe HTTP `Authorization` request header field:\r\n\r\n```\r\nGET /resource/1?b=1&a=2 HTTP/1.1\r\nHost: example.com:8000\r\nAuthorization: Hawk id=\"dh37fgj492je\", ts=\"1353832234\", nonce=\"j4h3g2\", ext=\"some-app-ext-data\", mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\"\r\n```\r\n\r\nThe server validates the request by calculating the request MAC again based on the request received and verifies the validity\r\nand scope of the **Hawk** credentials. If valid, the server responds with the requested resource.\r\n\r\n\r\n### Payload Validation\r\n\r\n**Hawk** provides optional payload validation. When generating the authentication header, the client calculates a payload hash\r\nusing the specified hash algorithm. The hash is calculated over the concatenated value of (each followed by a newline character):\r\n* `hawk.1.payload`\r\n* the content-type in lowercase, without any parameters (e.g. `application/json`)\r\n* 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)\r\n\r\nFor example:\r\n\r\n* Payload: `Thank you for flying Hawk`\r\n* Content Type: `text/plain`\r\n* Hash (sha256): `Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=`\r\n\r\nResults in the following input to the payload hash function (newline terminated values):\r\n\r\n```\r\nhawk.1.payload\r\ntext/plain\r\nThank you for flying Hawk\r\n\r\n```\r\n\r\nWhich produces the following hash value:\r\n\r\n```\r\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\r\n```\r\n\r\nThe client constructs the normalized request string (newline terminated values):\r\n\r\n```\r\nhawk.1.header\r\n1353832234\r\nj4h3g2\r\nPOST\r\n/resource?a=1&b=2\r\nexample.com\r\n8000\r\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\r\nsome-app-ext-data\r\n\r\n```\r\n\r\nThen calculates the request MAC and includes the **Hawk** key identifier, timestamp, nonce, payload hash, application specific data,\r\nand request MAC, with the request using the HTTP `Authorization` request header field:\r\n\r\n```\r\nPOST /resource/1 HTTP/1.1\r\nHost: example.com:8000\r\nAuthorization: Hawk id=\"dh37fgj492je\", ts=\"1353832234\", nonce=\"j4h3g2\", hash=\"Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\", ext=\"some-app-ext-data\", mac=\"aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw=\"\r\n```\r\n\r\nIt is up to the server if and when it validates the payload for any given request, based solely on it's security policy\r\nand the nature of the data included.\r\n\r\nIf the payload is available at the time of authentication, the server uses the hash value provided by the client to construct\r\nthe normalized string and validates the MAC. If the MAC is valid, the server calculates the payload hash and compares the value\r\nwith the provided payload hash in the header. In many cases, checking the MAC first is faster than calculating the payload hash.\r\n\r\nHowever, if the payload is not available at authentication time (e.g. too large to fit in memory, streamed elsewhere, or processed\r\nat a different stage in the application), the server may choose to defer payload validation for later by retaining the hash value\r\nprovided by the client after validating the MAC.\r\n\r\nIt is important to note that MAC validation does not mean the hash value provided by the client is valid, only that the value\r\nincluded in the header was not modified. Without calculating the payload hash on the server and comparing it to the value provided\r\nby the client, the payload may be modified by an attacker.\r\n\r\n\r\n## Response Payload Validation\r\n\r\n**Hawk** provides partial response payload validation. The server includes the `Server-Authorization` response header which enables the\r\nclient to authenticate the response and ensure it is talking to the right server. **Hawk** defines the HTTP `Server-Authorization` header\r\nas a response header using the exact same syntax as the `Authorization` request header field.\r\n\r\nThe header is contructed using the same process as the client's request header. The server uses the same credentials and other\r\nartifacts provided by the client to constructs the normalized request string. The `ext` and `hash` values are replaced with\r\nnew values based on the server response. The rest as identical to those used by the client.\r\n\r\nThe result MAC digest is included with the optional `hash` and `ext` values:\r\n\r\n```\r\nServer-Authorization: Hawk mac=\"XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=\", hash=\"f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=\", ext=\"response-specific\"\r\n```\r\n\r\n\r\n## Browser Support and Considerations\r\n\r\nA browser script is provided for including using a `<script>` tag in [lib/browser.js](/lib/browser.js).\r\n\r\n**Hawk** relies on the _Server-Authorization_ and _WWW-Authenticate_ headers in its response to communicate with the client.\r\nTherefore, in case of CORS requests, it is important to consider sending _Access-Control-Expose-Headers_ with the value\r\n_\"WWW-Authenticate, Server-Authorization\"_ on each response from your server. As explained in the\r\n[specifications](http://www.w3.org/TR/cors/#access-control-expose-headers-response-header), it will indicate that these headers\r\ncan safely be accessed by the client (using getResponseHeader() on the XmlHttpRequest object). Otherwise you will be met with a\r\n[\"simple response header\"](http://www.w3.org/TR/cors/#simple-response-header) which excludes these fields and would prevent the\r\nHawk client from authenticating the requests.You can read more about the why and how in this\r\n[article](http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server)\r\n\r\n\r\n# Single URI Authorization\r\n\r\nThere are cases in which limited and short-term access to a protected resource is granted to a third party which does not\r\nhave access to the shared credentials. For example, displaying a protected image on a web page accessed by anyone. **Hawk**\r\nprovides limited support for such URIs in the form of a _bewit_ - a URI query parameter appended to the request URI which contains\r\nthe necessary credentials to authenticate the request.\r\n\r\nBecause of the significant security risks involved in issuing such access, bewit usage is purposely limited only to GET requests\r\nand for a finite period of time. Both the client and server can issue bewit credentials, however, the server should not use the same\r\ncredentials as the client to maintain clear traceability as to who issued which credentials.\r\n\r\nIn order to simplify implementation, bewit credentials do not support single-use policy and can be replayed multiple times within\r\nthe granted access timeframe. \r\n\r\n\r\n## Bewit Usage Example\r\n\r\nServer code:\r\n\r\n```javascript\r\nvar Http = require('http');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Credentials lookup function\r\n\r\nvar credentialsFunc = function (id, callback) {\r\n\r\n var credentials = {\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256'\r\n };\r\n\r\n return callback(null, credentials);\r\n};\r\n\r\n// Create HTTP server\r\n\r\nvar handler = function (req, res) {\r\n\r\n Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {\r\n\r\n res.writeHead(!err ? 200 : 401, { 'Content-Type': 'text/plain' });\r\n res.end(!err ? 'Access granted' : 'Shoosh!');\r\n });\r\n};\r\n\r\nHttp.createServer(handler).listen(8000, 'example.com');\r\n```\r\n\r\nBewit code generation:\r\n\r\n```javascript\r\nvar Request = require('request');\r\nvar Hawk = require('hawk');\r\n\r\n\r\n// Client credentials\r\n\r\nvar credentials = {\r\n id: 'dh37fgj492je',\r\n key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r\n algorithm: 'sha256'\r\n}\r\n\r\n// Generate bewit\r\n\r\nvar duration = 60 * 5; // 5 Minutes\r\nvar bewit = Hawk.uri.getBewit('http://example.com:8080/resource/1?b=1&a=2', { credentials: credentials, ttlSec: duration, ext: 'some-app-data' });\r\nvar uri = 'http://example.com:8000/resource/1?b=1&a=2' + '&bewit=' + bewit;\r\n```\r\n\r\n\r\n# Security Considerations\r\n\r\nThe greatest sources of security risks are usually found not in **Hawk** but in the policies and procedures surrounding its use.\r\nImplementers are strongly encouraged to assess how this module addresses their security requirements. This section includes\r\nan incomplete list of security considerations that must be reviewed and understood before deploying **Hawk** on the server.\r\nMany of the protections provided in **Hawk** depends on whether and how they are used.\r\n\r\n### MAC Keys Transmission\r\n\r\n**Hawk** does not provide any mechanism for obtaining or transmitting the set of shared credentials required. Any mechanism used\r\nto obtain **Hawk** credentials must ensure that these transmissions are protected using transport-layer mechanisms such as TLS.\r\n\r\n### Confidentiality of Requests\r\n\r\nWhile **Hawk** provides a mechanism for verifying the integrity of HTTP requests, it provides no guarantee of request\r\nconfidentiality. Unless other precautions are taken, eavesdroppers will have full access to the request content. Servers should\r\ncarefully consider the types of data likely to be sent as part of such requests, and employ transport-layer security mechanisms\r\nto protect sensitive resources.\r\n\r\n### Spoofing by Counterfeit Servers\r\n\r\n**Hawk** provides limited verification of the server authenticity. When receiving a response back from the server, the server\r\nmay choose to include a response `Server-Authorization` header which the client can use to verify the response. However, it is up to\r\nthe server to determine when such measure is included, to up to the client to enforce that policy.\r\n\r\nA hostile party could take advantage of this by intercepting the client's requests and returning misleading or otherwise\r\nincorrect responses. Service providers should consider such attacks when developing services using this protocol, and should\r\nrequire transport-layer security for any requests where the authenticity of the resource server or of server responses is an issue.\r\n\r\n### Plaintext Storage of Credentials\r\n\r\nThe **Hawk** key functions the same way passwords do in traditional authentication systems. In order to compute the request MAC,\r\nthe server must have access to the key in plaintext form. This is in contrast, for example, to modern operating systems, which\r\nstore only a one-way hash of user credentials.\r\n\r\nIf 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\r\nto perform any action on behalf of any resource owner. Accordingly, it is critical that servers protect these keys from unauthorized\r\naccess.\r\n\r\n### Entropy of Keys\r\n\r\nUnless a transport-layer security protocol is used, eavesdroppers will have full access to authenticated requests and request\r\nMAC values, and will thus be able to mount offline brute-force attacks to recover the key used. Servers should be careful to\r\nassign keys which are long enough, and random enough, to resist such attacks for at least the length of time that the **Hawk**\r\ncredentials are valid.\r\n\r\nFor example, if the credentials are valid for two weeks, servers should ensure that it is not possible to mount a brute force\r\nattack that recovers the key in less than two weeks. Of course, servers are urged to err on the side of caution, and use the\r\nlongest key reasonable.\r\n\r\nIt is equally important that the pseudo-random number generator (PRNG) used to generate these keys be of sufficiently high\r\nquality. Many PRNG implementations generate number sequences that may appear to be random, but which nevertheless exhibit\r\npatterns or other weaknesses which make cryptanalysis or brute force attacks easier. Implementers should be careful to use\r\ncryptographically secure PRNGs to avoid these problems.\r\n\r\n### Coverage Limitations\r\n\r\nThe request MAC only covers the HTTP `Host` header and optionally the `Content-Type` header. It does not cover any other headers\r\nwhich can often affect how the request body is interpreted by the server. If the server behavior is influenced by the presence\r\nor value of such headers, an attacker can manipulate the request headers without being detected. Implementers should use the\r\n`ext` feature to pass application-specific information via the `Authorization` header which is protected by the request MAC.\r\n\r\nThe response authentication, when performed, only covers the response payload, content-type, and the request information \r\nprovided by the client in it's request (method, resource, timestamp, nonce, etc.). It does not cover the HTTP status code or\r\nany other response header field (e.g. Location) which can affect the client's behaviour.\r\n\r\n### Future Time Manipulation\r\n\r\nThe protocol relies on a clock sync between the client and server. To accomplish this, the server informs the client of its\r\ncurrent time when an invalid timestamp is received.\r\n\r\nIf an attacker is able to manipulate this information and cause the client to use an incorrect time, it would be able to cause\r\nthe client to generate authenticated requests using time in the future. Such requests will fail when sent by the client, and will\r\nnot likely leave a trace on the server (given the common implementation of nonce, if at all enforced). The attacker will then\r\nbe able to replay the request at the correct time without detection.\r\n\r\nThe client must only use the time information provided by the server if:\r\n* it was delivered over a TLS connection and the server identity has been verified, or\r\n* the `tsm` MAC digest calculated using the same client credentials over the timestamp has been verified.\r\n\r\n### Client Clock Poisoning\r\n\r\nWhen receiving a request with a bad timestamp, the server provides the client with its current time. The client must never use\r\nthe time received from the server to adjust its own clock, and must only use it to calculate an offset for communicating with\r\nthat particular server.\r\n\r\n### Bewit Limitations\r\n\r\nSpecial care must be taken when issuing bewit credentials to third parties. Bewit credentials are valid until expiration and cannot\r\nbe revoked or limited without using other means. Whatever resource they grant access to will be completely exposed to anyone with\r\naccess to the bewit credentials which act as bearer credentials for that particular resource. While bewit usage is limited to GET\r\nrequests only and therefore cannot be used to perform transactions or change server state, it can still be used to expose private\r\nand sensitive information.\r\n\r\n\r\n# Frequently Asked Questions\r\n\r\n### Where is the protocol specification?\r\n\r\nIf you are looking for some prose explaining how all this works, **this is it**. **Hawk** is being developed as an open source\r\nproject instead of a standard. In other words, the [code](/hueniverse/hawk/tree/master/lib) is the specification. Not sure about\r\nsomething? Open an issue!\r\n\r\n### Is it done?\r\n\r\nAt if version 0.10.0, **Hawk** is feature-complete. However, until this module reaches version 1.0.0 it is considered experimental\r\nand is likely to change. This also means your feedback and contribution are very welcome. Feel free to open issues with questions\r\nand suggestions.\r\n\r\n### Where can I find **Hawk** implementations in other languages?\r\n\r\n**Hawk**'s only reference implementation is provided in JavaScript as a node.js module. However, others are actively porting it to other\r\nplatforms. There is already a [PHP](https://github.com/alexbilbie/PHP-Hawk),\r\n[.NET](https://github.com/pcibraro/hawknet), and [JAVA](https://github.com/wealdtech/hawk) libraries available. The full list\r\nis maintained [here](https://github.com/hueniverse/hawk/issues?labels=port). Please add an issue if you are working on another\r\nport. A cross-platform test-suite is in the works.\r\n\r\n### Why isn't the algorithm part of the challenge or dynamically negotiated?\r\n\r\nThe algorithm used is closely related to the key issued as different algorithms require different key sizes (and other\r\nrequirements). While some keys can be used for multiple algorithm, the protocol is designed to closely bind the key and algorithm\r\ntogether as part of the issued credentials.\r\n\r\n### Why is Host and Content-Type the only headers covered by the request MAC?\r\n\r\nIt is really hard to include other headers. Headers can be changed by proxies and other intermediaries and there is no\r\nwell-established way to normalize them. Many platforms change the case of header field names and values. The only\r\nstraight-forward solution is to include the headers in some blob (say, base64 encoded JSON) and include that with the request,\r\nan approach taken by JWT and other such formats. However, that design violates the HTTP header boundaries, repeats information,\r\nand introduces other security issues because firewalls will not be aware of these \"hidden\" headers. In addition, any information\r\nrepeated must be compared to the duplicated information in the header and therefore only moves the problem elsewhere.\r\n\r\n### Why not just use HTTP Digest?\r\n\r\nDigest requires pre-negotiation to establish a nonce. This means you can't just make a request - you must first send\r\na protocol handshake to the server. This pattern has become unacceptable for most web services, especially mobile\r\nwhere extra round-trip are costly.\r\n\r\n### Why bother with all this nonce and timestamp business?\r\n\r\n**Hawk** is an attempt to find a reasonable, practical compromise between security and usability. OAuth 1.0 got timestamp\r\nand nonces halfway right but failed when it came to scalability and consistent developer experience. **Hawk** addresses\r\nit by requiring the client to sync its clock, but provides it with tools to accomplish it.\r\n\r\nIn general, replay protection is a matter of application-specific threat model. It is less of an issue on a TLS-protected\r\nsystem where the clients are implemented using best practices and are under the control of the server. Instead of dropping\r\nreplay protection, **Hawk** offers a required time window and an optional nonce verification. Together, it provides developers\r\nwith the ability to decide how to enforce their security policy without impacting the client's implementation.\r\n\r\n### What are `app` and `dlg` in the authorization header and normalized mac string?\r\n\r\nThe original motivation for **Hawk** was to replace the OAuth 1.0 use cases. This included both a simple client-server mode which\r\nthis module is specifically designed for, and a delegated access mode which is being developed separately in\r\n[Oz](https://github.com/hueniverse/oz). In addition to the **Hawk** use cases, Oz requires another attribute: the application id `app`.\r\nThis provides binding between the credentials and the application in a way that prevents an attacker from tricking an application\r\nto use credentials issued to someone else. It also has an optional 'delegated-by' attribute `dlg` which is the application id of the\r\napplication the credentials were directly issued to. The goal of these two additions is to allow Oz to utilize **Hawk** directly,\r\nbut with the additional security of delegated credentials.\r\n\r\n### What is the purpose of the static strings used in each normalized MAC input?\r\n\r\nWhen calculating a hash or MAC, a static prefix (tag) is added. The prefix is used to prevent MAC values from being\r\nused or reused for a purpose other than what they were created for (i.e. prevents switching MAC values between a request,\r\nresponse, and a bewit use cases). It also protects against expliots created after a potential change in how the protocol\r\ncreates the normalized string. For example, if a future version would switch the order of nonce and timestamp, it\r\ncan create an exploit opportunity for cases where the nonce is similar in format to a timestamp.\r\n\r\n### Does **Hawk** have anything to do with OAuth?\r\n\r\nShort answer: no.\r\n\r\n**Hawk** was originally proposed as the OAuth MAC Token specification. However, the OAuth working group in its consistent\r\nincompetence failed to produce a final, usable solution to address one of the most popular use cases of OAuth 1.0 - using it\r\nto authenticate simple client-server transactions (i.e. two-legged). As you can guess, the OAuth working group is still hard\r\nat work to produce more garbage.\r\n\r\n**Hawk** provides a simple HTTP authentication scheme for making client-server requests. It does not address the OAuth use case\r\nof delegating access to a third party. If you are looking for an OAuth alternative, check out [Oz](https://github.com/hueniverse/oz).\r\n\r\n\r\n# Acknowledgements\r\n\r\n**Hawk** is a derivative work of the [HTTP MAC Authentication Scheme](http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05) proposal\r\nco-authored by Ben Adida, Adam Barth, and Eran Hammer, which in turn was based on the OAuth 1.0 community specification.\r\n\r\nSpecial thanks to Ben Laurie for his always insightful feedback and advice.\r\n\r\nThe **Hawk** logo was created by [Chris Carrasco](http://chriscarrasco.com).\r\n",
"readmeFilename": "README.md",
- "_id": "hawk@0.10.2",
- "dist": {
- "shasum": "ebfc9448d2ff8821da7723faf735255f9c1833de"
+ "bugs": {
+ "url": "https://github.com/hueniverse/hawk/issues"
},
- "_from": "hawk@~0.10.2",
- "_resolved": "https://registry.npmjs.org/hawk/-/hawk-0.10.2.tgz"
+ "_id": "hawk@0.13.1",
+ "_from": "hawk@~0.13.0"
}
--- /dev/null
+// Load modules\r
+\r
+var Lab = require('lab');\r
+var Hawk = require('../lib');\r
+var Browser = require('../lib/browser');\r
+var LocalStorage = require('localStorage');\r
+\r
+\r
+// Declare internals\r
+\r
+var internals = {};\r
+\r
+\r
+// Test shortcuts\r
+\r
+var expect = Lab.expect;\r
+var before = Lab.before;\r
+var after = Lab.after;\r
+var describe = Lab.experiment;\r
+var it = Lab.test;\r
+\r
+\r
+describe('Browser', function () {\r
+\r
+ var credentialsFunc = function (id, callback) {\r
+\r
+ var credentials = {\r
+ id: id,\r
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
+ algorithm: (id === '1' ? 'sha1' : 'sha256'),\r
+ user: 'steve'\r
+ };\r
+\r
+ return callback(null, credentials);\r
+ };\r
+\r
+ it('should generate a header then successfully parse it (configuration)', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;\r
+ expect(req.authorization).to.exist;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it (node request)', function (done) {\r
+\r
+ var req = {\r
+ method: 'POST',\r
+ url: '/resource/4?filter=a',\r
+ headers: {\r
+ host: 'example.com:8080',\r
+ 'content-type': 'text/plain;x=y'\r
+ }\r
+ };\r
+\r
+ var payload = 'some not so random text';\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ 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'] });\r
+ req.headers.authorization = reqHeader.field;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);\r
+\r
+ var res = {\r
+ headers: {\r
+ 'content-type': 'text/plain'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });\r
+ expect(res.headers['server-authorization']).to.exist;\r
+\r
+ expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it (no server header options)', function (done) {\r
+\r
+ var req = {\r
+ method: 'POST',\r
+ url: '/resource/4?filter=a',\r
+ headers: {\r
+ host: 'example.com:8080',\r
+ 'content-type': 'text/plain;x=y'\r
+ }\r
+ };\r
+\r
+ var payload = 'some not so random text';\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ 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'] });\r
+ req.headers.authorization = reqHeader.field;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);\r
+\r
+ var res = {\r
+ headers: {\r
+ 'content-type': 'text/plain'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);\r
+ expect(res.headers['server-authorization']).to.exist;\r
+\r
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it (no server header)', function (done) {\r
+\r
+ var req = {\r
+ method: 'POST',\r
+ url: '/resource/4?filter=a',\r
+ headers: {\r
+ host: 'example.com:8080',\r
+ 'content-type': 'text/plain;x=y'\r
+ }\r
+ };\r
+\r
+ var payload = 'some not so random text';\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ 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'] });\r
+ req.headers.authorization = reqHeader.field;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);\r
+\r
+ var res = {\r
+ headers: {\r
+ 'content-type': 'text/plain'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header with stale ts and successfully authenticate on second call', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ Browser.utils.setNtpOffset(60 * 60 * 1000);\r
+ var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' });\r
+ req.authorization = header.field;\r
+ expect(req.authorization).to.exist;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.exist;\r
+ expect(err.message).to.equal('Stale timestamp');\r
+\r
+ var res = {\r
+ headers: {\r
+ 'www-authenticate': err.response.headers['WWW-Authenticate']\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);\r
+ expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true);\r
+ expect(Browser.utils.getNtpOffset()).to.equal(0);\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;\r
+ expect(req.authorization).to.exist;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header with stale ts and successfully authenticate on second call (manual localStorage)', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+ Browser.utils.setStorage(LocalStorage)\r
+\r
+ Browser.utils.setNtpOffset(60 * 60 * 1000);\r
+ var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' });\r
+ req.authorization = header.field;\r
+ expect(req.authorization).to.exist;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.exist;\r
+ expect(err.message).to.equal('Stale timestamp');\r
+\r
+ var res = {\r
+ headers: {\r
+ 'www-authenticate': err.response.headers['WWW-Authenticate']\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ expect(parseInt(LocalStorage.getItem('hawk_ntp_offset'))).to.equal(60 * 60 * 1000);\r
+ expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);\r
+ expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true);\r
+ expect(Browser.utils.getNtpOffset()).to.equal(0);\r
+ expect(parseInt(LocalStorage.getItem('hawk_ntp_offset'))).to.equal(0);\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;\r
+ expect(req.authorization).to.exist;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then fails to parse it (missing server header hash)', function (done) {\r
+\r
+ var req = {\r
+ method: 'POST',\r
+ url: '/resource/4?filter=a',\r
+ headers: {\r
+ host: 'example.com:8080',\r
+ 'content-type': 'text/plain;x=y'\r
+ }\r
+ };\r
+\r
+ var payload = 'some not so random text';\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ 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'] });\r
+ req.headers.authorization = reqHeader.field;\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);\r
+\r
+ var res = {\r
+ headers: {\r
+ 'content-type': 'text/plain'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);\r
+ expect(res.headers['server-authorization']).to.exist;\r
+\r
+ expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(false);\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it (with hash)', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it then validate payload', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(Hawk.server.authenticatePayload('hola!', credentials, artifacts)).to.be.true;\r
+ expect(Hawk.server.authenticatePayload('hello!', credentials, artifacts)).to.be.false;\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it (app)', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased' }).field;\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(artifacts.app).to.equal('asd23ased');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then successfully parse it (app, dlg)', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ 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;\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.not.exist;\r
+ expect(credentials.user).to.equal('steve');\r
+ expect(artifacts.ext).to.equal('some-app-data');\r
+ expect(artifacts.app).to.equal('asd23ased');\r
+ expect(artifacts.dlg).to.equal('23434szr3q4d');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header then fail authentication due to bad hash', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;\r
+ Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.exist;\r
+ expect(err.response.payload.message).to.equal('Bad payload hash');\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ it('should generate a header for one resource then fail to authenticate another', function (done) {\r
+\r
+ var req = {\r
+ method: 'GET',\r
+ url: '/resource/4?filter=a',\r
+ host: 'example.com',\r
+ port: 8080\r
+ };\r
+\r
+ credentialsFunc('123456', function (err, credentials) {\r
+\r
+ req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;\r
+ req.url = '/something/else';\r
+\r
+ Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {\r
+\r
+ expect(err).to.exist;\r
+ expect(credentials).to.exist;\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ describe('client', function () {\r
+\r
+ describe('#header', function () {\r
+\r
+ it('should return a valid authorization header (sha1)', function (done) {\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: '2983d45yun89q',\r
+ algorithm: 'sha1'\r
+ };\r
+\r
+ 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;\r
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');\r
+ done();\r
+ });\r
+\r
+ it('should return a valid authorization header (sha256)', function (done) {\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: '2983d45yun89q',\r
+ algorithm: 'sha256'\r
+ };\r
+\r
+ 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;\r
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');\r
+ done();\r
+ });\r
+\r
+ it('should return a valid authorization header (no ext)', function (done) {\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: '2983d45yun89q',\r
+ algorithm: 'sha256'\r
+ };\r
+\r
+ 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;\r
+ expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');\r
+ done();\r
+ });\r
+\r
+ it('should return an empty authorization header on missing options', function (done) {\r
+\r
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST').field;\r
+ expect(header).to.equal('');\r
+ done();\r
+ });\r
+\r
+ it('should return an empty authorization header on invalid credentials', function (done) {\r
+\r
+ var credentials = {\r
+ key: '2983d45yun89q',\r
+ algorithm: 'sha256'\r
+ };\r
+\r
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }).field;\r
+ expect(header).to.equal('');\r
+ done();\r
+ });\r
+\r
+ it('should return an empty authorization header on invalid algorithm', function (done) {\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: '2983d45yun89q',\r
+ algorithm: 'hmac-sha-0'\r
+ };\r
+\r
+ var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 }).field;\r
+ expect(header).to.equal('');\r
+ done();\r
+ });\r
+ });\r
+\r
+ describe('#authenticate', function () {\r
+\r
+ it('should return false on invalid header', function (done) {\r
+\r
+ var res = {\r
+ headers: {\r
+ 'server-authorization': 'Hawk mac="abc", bad="xyz"'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ expect(Browser.client.authenticate(res, {})).to.equal(false);\r
+ done();\r
+ });\r
+\r
+ it('should return false on invalid mac', function (done) {\r
+\r
+ var res = {\r
+ headers: {\r
+ 'content-type': 'text/plain',\r
+ 'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ var artifacts = {\r
+ method: 'POST',\r
+ host: 'example.com',\r
+ port: '8080',\r
+ resource: '/resource/4?filter=a',\r
+ ts: '1362336900',\r
+ nonce: 'eb5S_L',\r
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',\r
+ ext: 'some-app-data',\r
+ app: undefined,\r
+ dlg: undefined,\r
+ mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',\r
+ id: '123456'\r
+ };\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
+ algorithm: 'sha256',\r
+ user: 'steve'\r
+ };\r
+\r
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(false);\r
+ done();\r
+ });\r
+\r
+ it('should return true on ignoring hash', function (done) {\r
+\r
+ var res = {\r
+ headers: {\r
+ 'content-type': 'text/plain',\r
+ 'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ var artifacts = {\r
+ method: 'POST',\r
+ host: 'example.com',\r
+ port: '8080',\r
+ resource: '/resource/4?filter=a',\r
+ ts: '1362336900',\r
+ nonce: 'eb5S_L',\r
+ hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',\r
+ ext: 'some-app-data',\r
+ app: undefined,\r
+ dlg: undefined,\r
+ mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',\r
+ id: '123456'\r
+ };\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
+ algorithm: 'sha256',\r
+ user: 'steve'\r
+ };\r
+\r
+ expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);\r
+ done();\r
+ });\r
+\r
+ it('should fail on invalid WWW-Authenticate header format', function (done) {\r
+\r
+ var res = {\r
+ headers: {\r
+ 'www-authenticate': 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ expect(Browser.client.authenticate(res, {})).to.equal(false);\r
+ done();\r
+ });\r
+\r
+ it('should fail on invalid WWW-Authenticate header format', function (done) {\r
+\r
+ var credentials = {\r
+ id: '123456',\r
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',\r
+ algorithm: 'sha256',\r
+ user: 'steve'\r
+ };\r
+\r
+ var res = {\r
+ headers: {\r
+ 'www-authenticate': 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"'\r
+ },\r
+ getResponseHeader: function (header) {\r
+\r
+ return res.headers[header.toLowerCase()];\r
+ }\r
+ };\r
+\r
+ expect(Browser.client.authenticate(res, credentials)).to.equal(false);\r
+ done();\r
+ });\r
+ });\r
+ });\r
+\r
+ describe('#parseAuthorizationHeader', function (done) {\r
+\r
+ it('returns null on missing header', function (done) {\r
+\r
+ expect(Browser.utils.parseAuthorizationHeader()).to.equal(null);\r
+ done();\r
+ });\r
+\r
+ it('returns null on bad header syntax (structure)', function (done) {\r
+\r
+ expect(Browser.utils.parseAuthorizationHeader('Hawk')).to.equal(null);\r
+ done();\r
+ });\r
+\r
+ it('returns null on bad header syntax (parts)', function (done) {\r
+\r
+ expect(Browser.utils.parseAuthorizationHeader(' ')).to.equal(null);\r
+ done();\r
+ });\r
+\r
+ it('returns null on bad scheme name', function (done) {\r
+\r
+ expect(Browser.utils.parseAuthorizationHeader('Basic asdasd')).to.equal(null);\r
+ done();\r
+ });\r
+\r
+ it('returns null on bad attribute value', function (done) {\r
+\r
+ expect(Browser.utils.parseAuthorizationHeader('Hawk test="\t"', ['test'])).to.equal(null);\r
+ done();\r
+ });\r
+\r
+ it('returns null on duplicated attribute', function (done) {\r
+\r
+ expect(Browser.utils.parseAuthorizationHeader('Hawk test="a", test="b"', ['test'])).to.equal(null);\r
+ done();\r
+ });\r
+ });\r
+});\r
done();
});
- it('should return a valid authorization header (no ext)', function (done) {\r
+ it('should return a valid authorization header (no ext)', function (done) {
- var credentials = {\r
+ var credentials = {
id: '123456',
key: '2983d45yun89q',
- algorithm: 'sha256'\r
+ 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();\r
+ done();
});
it('should return an empty authorization header on missing options', function (done) {
app: undefined,
dlg: undefined,
mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
+ id: '123456'
+ };
+
+ var credentials = {
id: '123456',
- credentials:
- {
- id: '123456',
- key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
- algorithm: 'sha256',
- user: 'steve'
- }
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
};
- expect(Hawk.client.authenticate(res, artifacts)).to.equal(false);
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(false);
done();
});
app: undefined,
dlg: undefined,
mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
+ id: '123456'
+ };
+
+ var credentials = {
id: '123456',
- credentials:
- {
- id: '123456',
- key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
- algorithm: 'sha256',
- user: 'steve'
- }
+ key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
+ algorithm: 'sha256',
+ user: 'steve'
};
- expect(Hawk.client.authenticate(res, artifacts)).to.equal(true);
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
done();
});
it('should fail on invalid WWW-Authenticate header format', function (done) {
- var artifacts = {
- credentials: {
- id: '123456',
- key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
- algorithm: 'sha256',
- user: 'steve'
- }
+ 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 } }, artifacts)).to.equal(false);
+ expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, credentials)).to.equal(false);
done();
});
});
// Load modules
-var Lab = require('lab');\r
+var Lab = require('lab');
var Hawk = require('../lib');
// Test shortcuts
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
describe('Hawk', function () {
// Load modules
var Url = require('url');
-var Lab = require('lab');\r
+var Lab = require('lab');
var Hawk = require('../lib');
// Test shortcuts
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
describe('Hawk', function () {
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.hash, req.headers['content-type'])).to.equal(true);
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
var res = {
headers: {
}
};
- res.headers['server-authorization'] = Hawk.server.header(artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
+ 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, artifacts, { payload: 'some reply' })).to.equal(true);
+ expect(Hawk.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);
done();
});
});
});
- it('should generate a header then successfully parse it (no server header options)', function (done) {\r
+ it('should generate a header then successfully parse it (no server header options)', function (done) {
- var req = {\r
+ var req = {
method: 'POST',
url: '/resource/4?filter=a',
- headers: {\r
+ headers: {
host: 'example.com:8080',
- 'content-type': 'text/plain;x=y'\r
- }\r
+ 'content-type': 'text/plain;x=y'
+ }
};
var payload = 'some not so random text';
- credentialsFunc('123456', function (err, credentials) {\r
+ 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) {\r
+ 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.hash, req.headers['content-type'])).to.equal(true);
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
- var res = {\r
- headers: {\r
- 'content-type': 'text/plain'\r
- }\r
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ }
};
- res.headers['server-authorization'] = Hawk.server.header(artifacts);
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
expect(res.headers['server-authorization']).to.exist;
- expect(Hawk.client.authenticate(res, artifacts)).to.equal(true);
- done();\r
- });\r
- });\r
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
+ done();
+ });
+ });
});
- it('should generate a header then fails to parse it (missing server header hash)', function (done) {\r
+ it('should generate a header then fails to parse it (missing server header hash)', function (done) {
- var req = {\r
+ var req = {
method: 'POST',
url: '/resource/4?filter=a',
- headers: {\r
+ headers: {
host: 'example.com:8080',
- 'content-type': 'text/plain;x=y'\r
- }\r
+ 'content-type': 'text/plain;x=y'
+ }
};
var payload = 'some not so random text';
- credentialsFunc('123456', function (err, credentials) {\r
+ 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) {\r
+ 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.hash, req.headers['content-type'])).to.equal(true);
+ expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
- var res = {\r
- headers: {\r
- 'content-type': 'text/plain'\r
- }\r
+ var res = {
+ headers: {
+ 'content-type': 'text/plain'
+ }
};
- res.headers['server-authorization'] = Hawk.server.header(artifacts);
+ res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
expect(res.headers['server-authorization']).to.exist;
- expect(Hawk.client.authenticate(res, artifacts, { payload: 'some reply' })).to.equal(false);
- done();\r
- });\r
- });\r
+ expect(Hawk.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(false);
+ done();
+ });
+ });
});
it('should generate a header then successfully parse it (with hash)', function (done) {
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.hash)).to.be.true;
- expect(Hawk.server.authenticatePayload('hello!', credentials, artifacts.hash)).to.be.false;
+ expect(Hawk.server.authenticatePayload('hola!', credentials, artifacts)).to.be.true;
+ expect(Hawk.server.authenticatePayload('hello!', credentials, artifacts)).to.be.false;
done();
});
});
});
});
- it('should generate a header then successfully parse it (app, dlg)', function (done) {\r
+ it('should generate a header then successfully parse it (app, dlg)', function (done) {
- var req = {\r
+ var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
- port: 8080\r
+ port: 8080
};
- credentialsFunc('123456', function (err, credentials) {\r
+ 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) {\r
+ 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();\r
- });\r
- });\r
+ done();
+ });
+ });
});
it('should generate a header then fail authentication due to bad hash', function (done) {
// Load modules
-var Lab = require('lab');\r
+var Lab = require('lab');
var Hoek = require('hoek');
var Hawk = require('../lib');
resource: '/resource?a=1&b=2',
host: 'example.com',
port: 8000,
- hash: Hawk.crypto.calculateHash(payloadOptions.payload, credentials.algorithm, payloadOptions.contentType),
+ hash: Hawk.crypto.calculatePayloadHash(payloadOptions.payload, credentials.algorithm, payloadOptions.contentType),
ext: options.ext
});
// Load modules
var Url = require('url');
-var Lab = require('lab');\r
+var Lab = require('lab');
var Hawk = require('../lib');
// Test shortcuts
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
describe('Hawk', function () {
var header = err.response.headers['WWW-Authenticate'];
var ts = header.match(/^Hawk ts\=\"(\d+)\"\, tsm\=\"([^\"]+)\"\, error=\"Stale timestamp\"$/);
var now = Hawk.utils.now();
- expect(parseInt(ts[1], 10)).to.be.within(now - 1, now + 1);
+ expect(parseInt(ts[1], 10) * 1000).to.be.within(now - 1000, now + 1000);
var res = {
headers: {
}
};
- expect(Hawk.client.authenticate(res, artifacts)).to.equal(true);
+ expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
done();
});
});
done();
});
+ it('should return an empty authorization header on missing credentials', function (done) {
+
+ var header = Hawk.server.header(null, {});
+ expect(header).to.equal('');
+ done();
+ });
+
it('should return an empty authorization header on invalid credentials', function (done) {
- var artifacts = {
- credentials: {
- key: '2983d45yun89q'
- }
+ var credentials = {
+ key: '2983d45yun89q'
};
- var header = Hawk.server.header(artifacts);
+ var header = Hawk.server.header(credentials);
expect(header).to.equal('');
done();
});
it('should return an empty authorization header on invalid algorithm', function (done) {
var artifacts = {
- id: '123456',
- credentials: {
- key: '2983d45yun89q',
- algorithm: 'hmac-sha-0'
- }
+ id: '123456'
+ };
+
+ var credentials = {
+ key: '2983d45yun89q',
+ algorithm: 'hmac-sha-0'
};
- var header = Hawk.server.header(artifacts);
+ var header = Hawk.server.header(credentials, artifacts);
expect(header).to.equal('');
done();
});
// Load modules
var Http = require('http');
-var Lab = require('lab');\r
+var Lab = require('lab');
var Hawk = require('../lib');
// Test shortcuts
-var expect = Lab.expect;\r
-var before = Lab.before;\r
-var after = Lab.after;\r
-var describe = Lab.experiment;\r
-var it = Lab.test;\r
+var expect = Lab.expect;
+var before = Lab.before;
+var after = Lab.after;
+var describe = Lab.experiment;
+var it = Lab.test;
describe('Hawk', function () {
});
});
- it('should generate a bewit then successfully authenticate it (no ext)', function (done) {\r
+ it('should generate a bewit then successfully authenticate it (no ext)', function (done) {
- var req = {\r
+ var req = {
method: 'GET',
url: '/resource/4?a=1&b=2',
host: 'example.com',
- port: 80\r
+ port: 80
};
- credentialsFunc('123456', function (err, credentials) {\r
+ 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) {\r
+ Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.not.exist;
expect(credentials.user).to.equal('steve');
- done();\r
- });\r
- });\r
+ done();
+ });
+ });
});
it('should successfully authenticate a request (last param)', function (done) {
var exp = Math.floor(Hawk.utils.now() / 1000) + 60;
var ext = 'some-app-data';
- var mac = Hawk.crypto.calculateMac('bewit', {
- credentials: credentials,
+ var mac = Hawk.crypto.calculateMac('bewit', credentials, {
timestamp: exp,
nonce: '',
method: req.method,
describe('Utils', function () {
- describe('#parseHost', function () {\r
-\r
- it('returns port 80 for non tls node request', function (done) {\r
-\r
- var req = {\r
+ describe('#parseHost', function () {
+
+ it('returns port 80 for non tls node request', function (done) {
+
+ var req = {
method: 'POST',
url: '/resource/4?filter=a',
- headers: {\r
+ headers: {
host: 'example.com',
- 'content-type': 'text/plain;x=y'\r
- }\r
+ 'content-type': 'text/plain;x=y'
+ }
};
- expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(80);\r
- done();\r
- });\r
-\r
- it('returns port 443 for non tls node request', function (done) {\r
-\r
- var req = {\r
+ 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: {\r
+ headers: {
host: 'example.com',
- 'content-type': 'text/plain;x=y'\r
- },\r
- connection: {\r
- encrypted: true\r
- }\r
+ 'content-type': 'text/plain;x=y'
+ },
+ connection: {
+ encrypted: true
+ }
};
- expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);\r
- done();\r
- });\r
+ expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
+ done();
+ });
});
describe('#version', function () {
done();
});
});
+
+ describe('#unauthorized', function () {\r
+
+ it('returns a hawk 401', function (done) {\r
+
+ expect(Hawk.utils.unauthorized('kaboom').response.headers['WWW-Authenticate']).to.equal('Hawk error="kaboom"');
+ done();\r
+ });\r
+ });\r
});
});
--- /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
+Makefile
+deps
+docs
+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 is a node.js library that has client and server components
+for Joyent's `HTTP Signature Scheme`.
+
+## 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 actaully 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
+
+ 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
+
+ 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 methodoloy, 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 digitalSignature
+ params := 1#(keyId | [algorithm] | [headers] | [ext])
+ digitalSignature := plain-string
+
+ keyId := "keyId" "=" <"> plain-string <">
+ algorithm := "algorithm" "=" <"> plain-string <">
+ headers := "headers" "=" <"> 1#headers-value <">
+ ext := "ext" "=" <"> 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.
+
+### Digital Signature
+
+The `digitalSignature` portion of the credentials is a REQUIRED field, and is
+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 result is then `Base64` encoded.
+
+### 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, and separate with an ASCII newline `\n`. The last header in the list
+MUST NOT include a trailing ASCII newline.
+
+# Example Requests
+
+All requests refer to the following request (body ommitted):
+
+ 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
+
+## Default parameterization
+
+ Authorization: Signature keyId="123" Base64(RSA-SHA256(Tue, 07 Jun 2011 20:51:35 GMT))
+
+## Header List
+
+ Authorization: Signature keyId="123",headers="content-type Date content-md5" Base64(RSA-SHA256(Tue, 07 Jun 2011 20:51:35 GMT))
+
+The client would compose the signing string as (`+ "\n"` inserted for
+readability):
+
+ application/json + "\n"
+ Tue, 07 Jun 2011 20:51:35 GMT + "\n"
+ h0auK8hnYJKmHTLhKtMTkQ==
+
+## Algorithm
+
+ Authorization: Signature keyId="123",algorithm="hmac-sha1" Base64(HMAC-SHA1(Tue, 07 Jun 2011 20:51:35 GMT))
+
+# Signing Algorithms
+
+Currently supported algorithm names are:
+
+* rsa-sha1
+* rsa-sha256
+* rsa-sha512
+* 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/repudiaton, 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 specfiications
+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:
+
+```
+Thu, 05 Jan 2012 21:31:40 GMT
+```
+
+The Authorization header would be:
+
+ Authorization: Signature keyId="Test",algorithm="rsa-sha256" MDyO5tSvin5FBVdq3gMBTwtVgE8U/JpzSwFvY7gu7Q2tiZ5TvfHzf/RzmRoYwO8PoV1UGaw6IMwWzxDQkcoYOwvG/w4ljQBBoNusO/mYSvKrbqxUmZi8rNtrMcb82MS33bai5IeLnOGl31W1UbL4qE/wL8U9wCPGRJlCFLsTgD8=
+
+### All Headers
+
+Parameterized to include all headers, the string to sign would be:
+
+```
+/foo?param=value&pet=dog HTTP/1.1
+example.com
+Thu, 05 Jan 2012 21:31:40 GMT
+application/json
+Sd/dVLAcvNLSq16eXua5uQ==
+18
+```
+
+The Authorization header would be:
+
+ Authorization: Signature
+ keyId="Test",algorithm="rsa-sha256",headers="request-line host date content-type content-md5 content-length" gVrKP7wVh1+FmWbNlhj0pNXIe9XmeOA6EcnoOKAvUILnwaMFzaKaam9UmeDPwjC9TdT+jSRqjtyZE49kZcSpYAHxGlPQ4ziXFRfPprlN/3Xwg3sUOGqbBiS3WFuY3QOOWv4tzc5p70g74U/QvHNNiYMcjoz89vRJhefbFSNwCDs=
+
--- /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,
+
+ 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,
+ Signature: 2
+};
+
+var ParamsState = {
+ Name: 0,
+ Value: 1
+};
+
+
+
+///--- 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: {},
+ signature: '',
+ 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:
+ if (c === '"') {
+ parsed.params[tmpName] = '';
+ tmpValue = '';
+ substate = ParamsState.Value;
+ } else if (c === ' ') {
+ state = State.Signature;
+ } else if (c !== '=' && c !== ',') {
+ tmpName += c;
+ }
+ break;
+
+ case ParamsState.Value:
+ if (c === '"') {
+ parsed.params[tmpName] = tmpValue;
+ tmpName = '';
+ substate = ParamsState.Name;
+ } else {
+ tmpValue += c;
+ }
+ break;
+
+ default:
+ throw new Error('Invalid substate');
+ }
+ break;
+
+
+ case State.Signature:
+ parsed.signature += c;
+ 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.signature)
+ throw new InvalidHeaderError('signature was empty');
+
+ // 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;
+
+ var value;
+ if (h !== 'request-line') {
+ value = request.headers[h];
+ if (!value)
+ throw new MissingHeaderError(h + ' was not in the request');
+ } else {
+ value =
+ request.method + ' ' + request.url + ' HTTP/' + request.httpVersion;
+ }
+
+ parsed.signingString += value;
+ 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" %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'.
+ * @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');
+
+ if (!request.getHeader('Date'))
+ request.setHeader('Date', _rfc1123());
+ if (!options.headers)
+ options.headers = ['date'];
+ if (!options.algorithm)
+ options.algorithm = 'rsa-sha256';
+
+ 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();
+ request.getHeader(h);
+
+ var value = request.getHeader(h);
+ if (!value) {
+ if (h === 'request-line') {
+ value = request.method + ' ' + request.path + ' HTTP/1.1';
+ } else {
+ throw new MissingHeaderError(h + ' was not in the request');
+ }
+ }
+
+ stringToSign += value;
+ 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;
+ }
+
+
+};
--- /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].toLowerCase(), key);
+ hmac.update(parsedSignature.signingString);
+ return (hmac.digest('base64') === parsedSignature.signature);
+ } else {
+ var verify = crypto.createVerify(alg[0]);
+ verify.update(parsedSignature.signingString);
+ return verify.verify(key, parsedSignature.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"
+ },
+ "readme": "node-asn1 is a library for encoding and decoding ASN.1 datatypes in pure JS.\nCurrently BER encoding is supported; at some point I'll likely have to do DER.\n\n## Usage\n\nMostly, if you're *actually* needing to read and write ASN.1, you probably don't\nneed this readme to explain what and why. If you have no idea what ASN.1 is,\nsee this: ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc\n\nThe source is pretty much self-explanatory, and has read/write methods for the\ncommon types out there.\n\n### Decoding\n\nThe following reads an ASN.1 sequence with a boolean.\n\n var Ber = require('asn1').Ber;\n\n var reader = new Ber.Reader(new Buffer([0x30, 0x03, 0x01, 0x01, 0xff]));\n\n reader.readSequence();\n console.log('Sequence len: ' + reader.length);\n if (reader.peek() === Ber.Boolean)\n console.log(reader.readBoolean());\n\n### Encoding\n\nThe following generates the same payload as above.\n\n var Ber = require('asn1').Ber;\n\n var writer = new Ber.Writer();\n\n writer.startSequence();\n writer.writeBoolean(true);\n writer.endSequence();\n\n console.log(writer.buffer);\n\n## Installation\n\n npm install asn1\n\n## License\n\nMIT.\n\n## Bugs\n\nSee <https://github.com/mcavage/node-asn1/issues>.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/mcavage/node-asn1/issues"
+ },
+ "_id": "asn1@0.1.11",
+ "_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;
+
+
+
+///--- 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
+ });
+ }
+ }
+}
+
+
+
+///--- 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, type),
+ 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);
+}
+
+
+function object(arg, name) {
+ _assert(arg, 'object', name);
+}
+
+
+function stream(arg, name) {
+ if (!(arg instanceof Stream)) {
+ throw new assert.AssertionError({
+ message: _(TYPE_REQUIRED, name, type),
+ actual: typeof (arg),
+ expected: 'Stream',
+ operator: 'instanceof',
+ stackStartFunction: buffer
+ });
+ }
+}
+
+
+function string(arg, name) {
+ _assert(arg, 'string', name);
+}
+
+
+
+///--- Exports
+
+module.exports = {
+ bool: bool,
+ buffer: buffer,
+ func: func,
+ number: number,
+ object: object,
+ stream: stream,
+ string: string
+};
+
+
+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.2",
+ "main": "./assert.js",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": ">=0.6"
+ },
+ "readme": "# node-assert-plus\n\nThis library is a super small wrapper over node's assert module that has two\nthings: (1) the ability to disable assertions with the environment variable\nNODE_NDEBUG, and (2) some API wrappers for argument testing. Like\n`assert.string(myArg, 'myArg')`. As a simple example, most of my code looks\nlike this:\n\n var assert = require('assert-plus');\n\n function fooAccount(options, callback) {\n\t assert.object(options, 'options');\n\t\tassert.number(options.id, 'options.id);\n\t\tassert.bool(options.isManager, 'options.isManager');\n\t\tassert.string(options.name, 'options.name');\n\t\tassert.arrayOfString(options.email, 'options.email');\n\t\tassert.func(callback, 'callback');\n\n // Do stuff\n\t\tcallback(null, {});\n }\n\n# API\n\nAll methods that *aren't* part of node's core assert API are simply assumed to\ntake an argument, and then a string 'name' that's not a message; `AssertionError`\nwill be thrown if the assertion fails with a message like:\n\n AssertionError: foo (string) is required\n\tat test (/home/mark/work/foo/foo.js:3:9)\n\tat Object.<anonymous> (/home/mark/work/foo/foo.js:15:1)\n\tat Module._compile (module.js:446:26)\n\tat Object..js (module.js:464:10)\n\tat Module.load (module.js:353:31)\n\tat Function._load (module.js:311:12)\n\tat Array.0 (module.js:484:10)\n\tat EventEmitter._tickCallback (node.js:190:38)\n\nfrom:\n\n function test(foo) {\n\t assert.string(foo, 'foo');\n }\n\nThere you go. You can check that arrays are of a homogenous type with `Arrayof$Type`:\n\n function test(foo) {\n\t assert.arrayOfString(foo, 'foo');\n }\n\nYou can assert IFF an argument is not `undefined` (i.e., an optional arg):\n\n assert.optionalString(foo, 'foo');\n\nLastly, you can opt-out of assertion checking altogether by setting the\nenvironment variable `NODE_NDEBUG=1`. This is pseudo-useful if you have\nlots of assertions, and don't want to pay `typeof ()` taxes to v8 in\nproduction.\n\nThe complete list of APIs is:\n\n* assert.bool\n* assert.buffer\n* assert.func\n* assert.number\n* assert.object\n* assert.string\n* assert.arrayOfBool\n* assert.arrayOfFunc\n* assert.arrayOfNumber\n* assert.arrayOfObject\n* assert.arrayOfString\n* assert.optionalBool\n* assert.optionalBuffer\n* assert.optionalFunc\n* assert.optionalNumber\n* assert.optionalObject\n* assert.optionalString\n* assert.optionalArrayOfBool\n* assert.optionalArrayOfFunc\n* assert.optionalArrayOfNumber\n* assert.optionalArrayOfObject\n* assert.optionalArrayOfString\n* assert.AssertionError\n* assert.fail\n* assert.ok\n* assert.equal\n* assert.notEqual\n* assert.deepEqual\n* assert.notDeepEqual\n* assert.strictEqual\n* assert.notStrictEqual\n* assert.throws\n* assert.doesNotThrow\n* assert.ifError\n\n# Installation\n\n npm install assert-plus\n\n## License\n\nThe MIT License (MIT)\nCopyright (c) 2012 Mark Cavage\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n## Bugs\n\nSee <https://github.com/mcavage/node-assert-plus/issues>.\n",
+ "readmeFilename": "README.md",
+ "_id": "assert-plus@0.1.2",
+ "_from": "assert-plus@0.1.2"
+}
--- /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.
+
+CTYPE 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.2",
+ "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",
+ "readme": "Node-CType is a way to read and write binary data in structured and easy to use\nformat. Its name comes from the C header file.\n\nTo get started, simply clone the repository or use npm to install it. Once it is\nthere, simply require it.\n\ngit clone git://github.com/rmustacc/node-ctype\nnpm install ctype\nvar mod_ctype = require('ctype')\n\n\nThere are two APIs that you can use, depending on what abstraction you'd like.\nThe low level API let's you read and write individual integers and floats from\nbuffers. The higher level API let's you read and write structures of these. To\nillustrate this, let's looks look at how we would read and write a binary\nencoded x,y point.\n\nIn C we would define this structure as follows:\n\ntypedef struct point {\n\tuint16_t\tp_x;\n\tuint16_t\tp_y;\n} point_t;\n\nTo read a binary encoded point from a Buffer, we first need to create a CType\nparser (where we specify the endian and other options) and add the typedef.\n\nvar parser = new mod_ctype.Parser({ endian: 'big' });\nparser.typedef('point_t', [\n\t{ x: { type: 'uint16_t' } },\n\t{ y: { type: 'uint16_t' } }\n]);\n\nFrom here, given a buffer buf and an offset into it, we can read a point.\n\nvar out = parser.readData([ { point: { type: 'point_t' } } ], buffer, 0);\nconsole.log(out);\n{ point: { x: 23, y: 42 } }\n\nAnother way to get the same information would be to use the low level methods.\nNote that these require you to manually deal with the offset. Here's how we'd\nget the same values of x and y from the buffer.\n\nvar x = mod_ctype.ruint16(buf, 'big', 0);\nvar y = mod_ctype.ruint16(buf, 'big', 2);\nconsole.log(x + ', ' + y);\n23, 42\n\nThe true power of this API comes from the ability to define and nest typedefs,\njust as you would in C. By default, the following types are defined by default.\nNote that they return a Number, unless indicated otherwise.\n\n * int8_t\n * int16_t\n * int32_t\n * int64_t (returns an array where val[0] << 32 + val[1] would be the value)\n * uint8_t\n * uint16_t\n * uint32_t\n * uint64_t (returns an array where val[0] << 32 + val[1] would be the value)\n * float\n * double\n * char (either returns a buffer with that character or a uint8_t)\n * 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)\n\n\nctf2json integration:\n\nNode-CType supports consuming the output of ctf2json. Once you read in a JSON file,\nall you have to do to add all the definitions it contains is:\n\nvar data, parser;\ndata = JSON.parse(parsedJSONData);\nparser = mod_ctype.parseCTF(data, { endian: 'big' });\n\nFor more documentation, see the file README.old. Full documentation is in the\nprocess of being rewritten as a series of manual pages which will be available\nin the repository and online for viewing.\n\nTo read the ctio manual page simple run, from the root of the workspace:\n\nman -Mman -s 3ctype ctio\n",
+ "readmeFilename": "README",
+ "_id": "ctype@0.5.2",
+ "_from": "ctype@0.5.2"
+}
--- /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
+{ "metadata":
+ {
+ "ctf2json_version": "1.0",
+ "created_at": 1316563626,
+ "derived_from": "/lib/libc.so",
+ "ctf_version": 2,
+ "requested_types": [ "float" ]
+ },
+"data":
+ [
+ { "name": "float", "float": { "length": 4 } }
+ ]
+}
--- /dev/null
+{ "metadata":
+ {
+ "ctf2json_version": "1.0",
+ "created_at": 1316563631,
+ "derived_from": "/lib/libc.so",
+ "ctf_version": 2,
+ "requested_types": [ "int" ]
+ },
+"data":
+ [
+ { "name": "int", "integer": { "length": 4, "signed": true } }
+ ]
+}
--- /dev/null
+{ "metadata":
+ {
+ "ctf2json_version": "1.0",
+ "created_at": 1316563573,
+ "derived_from": "/lib/libc.so",
+ "ctf_version": 2,
+ "requested_types": [ "psinfo_t" ]
+ },
+"data":
+ [
+ { "name": "int", "integer": { "length": 4, "signed": true } },
+ { "name": "char", "integer": { "length": 1, "signed": true } },
+ { "name": "unsigned short", "integer": { "length": 2, "signed": false } },
+ { "name": "long", "integer": { "length": 4, "signed": true } },
+ { "name": "unsigned", "integer": { "length": 4, "signed": false } },
+ { "name": "size_t", "typedef": "unsigned" },
+ { "name": "unsigned long", "integer": { "length": 4, "signed": false } },
+ { "name": "time_t", "typedef": "long" },
+ { "name": "struct timespec", "struct": [
+ { "name": "tv_sec", "type": "time_t" },
+ { "name": "tv_nsec", "type": "long" }
+ ] },
+ { "name": "zoneid_t", "typedef": "long" },
+ { "name": "taskid_t", "typedef": "long" },
+ { "name": "dev_t", "typedef": "unsigned long" },
+ { "name": "uid_t", "typedef": "unsigned" },
+ { "name": "gid_t", "typedef": "unsigned" },
+ { "name": "timestruc_t", "typedef": "struct timespec" },
+ { "name": "short", "integer": { "length": 2, "signed": true } },
+ { "name": "projid_t", "typedef": "long" },
+ { "name": "ushort_t", "typedef": "unsigned short" },
+ { "name": "poolid_t", "typedef": "long" },
+ { "name": "uintptr_t", "typedef": "unsigned" },
+ { "name": "id_t", "typedef": "long" },
+ { "name": "pid_t", "typedef": "long" },
+ { "name": "processorid_t", "typedef": "int" },
+ { "name": "psetid_t", "typedef": "int" },
+ { "name": "struct lwpsinfo", "struct": [
+ { "name": "pr_flag", "type": "int" },
+ { "name": "pr_lwpid", "type": "id_t" },
+ { "name": "pr_addr", "type": "uintptr_t" },
+ { "name": "pr_wchan", "type": "uintptr_t" },
+ { "name": "pr_stype", "type": "char" },
+ { "name": "pr_state", "type": "char" },
+ { "name": "pr_sname", "type": "char" },
+ { "name": "pr_nice", "type": "char" },
+ { "name": "pr_syscall", "type": "short" },
+ { "name": "pr_oldpri", "type": "char" },
+ { "name": "pr_cpu", "type": "char" },
+ { "name": "pr_pri", "type": "int" },
+ { "name": "pr_pctcpu", "type": "ushort_t" },
+ { "name": "pr_pad", "type": "ushort_t" },
+ { "name": "pr_start", "type": "timestruc_t" },
+ { "name": "pr_time", "type": "timestruc_t" },
+ { "name": "pr_clname", "type": "char [8]" },
+ { "name": "pr_name", "type": "char [16]" },
+ { "name": "pr_onpro", "type": "processorid_t" },
+ { "name": "pr_bindpro", "type": "processorid_t" },
+ { "name": "pr_bindpset", "type": "psetid_t" },
+ { "name": "pr_lgrp", "type": "int" },
+ { "name": "pr_filler", "type": "int [4]" }
+ ] },
+ { "name": "lwpsinfo_t", "typedef": "struct lwpsinfo" },
+ { "name": "struct psinfo", "struct": [
+ { "name": "pr_flag", "type": "int" },
+ { "name": "pr_nlwp", "type": "int" },
+ { "name": "pr_pid", "type": "pid_t" },
+ { "name": "pr_ppid", "type": "pid_t" },
+ { "name": "pr_pgid", "type": "pid_t" },
+ { "name": "pr_sid", "type": "pid_t" },
+ { "name": "pr_uid", "type": "uid_t" },
+ { "name": "pr_euid", "type": "uid_t" },
+ { "name": "pr_gid", "type": "gid_t" },
+ { "name": "pr_egid", "type": "gid_t" },
+ { "name": "pr_addr", "type": "uintptr_t" },
+ { "name": "pr_size", "type": "size_t" },
+ { "name": "pr_rssize", "type": "size_t" },
+ { "name": "pr_pad1", "type": "size_t" },
+ { "name": "pr_ttydev", "type": "dev_t" },
+ { "name": "pr_pctcpu", "type": "ushort_t" },
+ { "name": "pr_pctmem", "type": "ushort_t" },
+ { "name": "pr_start", "type": "timestruc_t" },
+ { "name": "pr_time", "type": "timestruc_t" },
+ { "name": "pr_ctime", "type": "timestruc_t" },
+ { "name": "pr_fname", "type": "char [16]" },
+ { "name": "pr_psargs", "type": "char [80]" },
+ { "name": "pr_wstat", "type": "int" },
+ { "name": "pr_argc", "type": "int" },
+ { "name": "pr_argv", "type": "uintptr_t" },
+ { "name": "pr_envp", "type": "uintptr_t" },
+ { "name": "pr_dmodel", "type": "char" },
+ { "name": "pr_pad2", "type": "char [3]" },
+ { "name": "pr_taskid", "type": "taskid_t" },
+ { "name": "pr_projid", "type": "projid_t" },
+ { "name": "pr_nzomb", "type": "int" },
+ { "name": "pr_poolid", "type": "poolid_t" },
+ { "name": "pr_zoneid", "type": "zoneid_t" },
+ { "name": "pr_contract", "type": "id_t" },
+ { "name": "pr_filler", "type": "int [1]" },
+ { "name": "pr_lwp", "type": "lwpsinfo_t" }
+ ] },
+ { "name": "psinfo_t", "typedef": "struct psinfo" }
+ ]
+}
--- /dev/null
+{ "metadata":
+ {
+ "ctf2json_version": "1.0",
+ "created_at": 1316563648,
+ "derived_from": "/lib/libc.so",
+ "ctf_version": 2,
+ "requested_types": [ "timestruc_t" ]
+ },
+"data":
+ [
+ { "name": "long", "integer": { "length": 4, "signed": true } },
+ { "name": "time_t", "typedef": "long" },
+ { "name": "struct timespec", "struct": [
+ { "name": "tv_sec", "type": "time_t" },
+ { "name": "tv_nsec", "type": "long" }
+ ] },
+ { "name": "timestruc_t", "typedef": "struct timespec" }
+ ]
+}
--- /dev/null
+/*
+ * Test several conditions that should always cause us to throw.
+ */
+var mod_assert = require('assert');
+var mod_ctype = require('../../ctype.js');
+
+var cases = [
+{ json: { }, msg: 'bad JSON - no metadata or data' },
+{ json: { metadata: {} }, msg: 'bad JSON - bad metadata section' },
+{ json: { metadata: { 'JSON version': [] } },
+ msg: 'bad JSON - bad JSON version' },
+{ json: { metadata: { 'JSON version': 2 } },
+ msg: 'bad JSON - bad JSON version' },
+{ json: { metadata: { 'JSON version': '100.20' } },
+ msg: 'bad JSON - bad JSON version' },
+{ json: { metadata: { 'JSON version': '1.0' } },
+ msg: 'missing data section' },
+{ json: { metadata: { 'JSON version': '1.0' }, data: 1 },
+ msg: 'invalid data section' },
+{ json: { metadata: { 'JSON version': '1.0' }, data: 1.1 },
+ msg: 'invalid data section' },
+{ json: { metadata: { 'JSON version': '1.0' }, data: '1.1' },
+ msg: 'invalid data section' },
+{ json: { metadata: { 'JSON version': '1.0' }, data: {} },
+ msg: 'invalid data section' }
+];
+
+function test()
+{
+ var ii;
+
+ for (ii = 0; ii < cases.length; ii++) {
+ mod_assert.throws(function () {
+ mod_ctype.parseCTF(cases[ii].json);
+ }, Error, cases[ii].msg);
+ }
+}
+
+test();
--- /dev/null
+var mod_fs = require('fs');
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var data, parser;
+
+ data = JSON.parse(mod_fs.readFileSync('./float.json').toString());
+ parser = mod_ctype.parseCTF(data, { endian: 'big' });
+ mod_assert.deepEqual(parser.lstypes(), {});
+}
+
+test();
--- /dev/null
+var mod_fs = require('fs');
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var data, parser;
+
+ data = JSON.parse(mod_fs.readFileSync('./int.json').toString());
+ parser = mod_ctype.parseCTF(data, { endian: 'big' });
+ mod_assert.deepEqual(parser.lstypes(), { 'int': 'int32_t' });
+}
+
+test();
--- /dev/null
+var mod_fs = require('fs');
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+/*
+ * This is too unwieldly to actually write out. Just make sure we can parse it
+ * without errrors.
+ */
+function test()
+{
+ var data;
+
+ data = JSON.parse(mod_fs.readFileSync('./psinfo.json').toString());
+ mod_ctype.parseCTF(data, { endian: 'big' });
+}
+
+test();
--- /dev/null
+var mod_fs = require('fs');
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var data, parser;
+
+ data = JSON.parse(mod_fs.readFileSync('./struct.json').toString());
+ parser = mod_ctype.parseCTF(data, { endian: 'big' });
+ mod_assert.deepEqual(parser.lstypes(), { 'long': 'int32_t',
+ 'time_t': 'long',
+ 'timestruc_t': 'struct timespec',
+ 'struct timespec': [ { 'tv_sec': { 'type': 'time_t' } },
+ { 'tv_nsec': { 'type': 'long' } } ] });
+}
+
+test();
--- /dev/null
+var mod_fs = require('fs');
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var data, parser;
+
+ data = JSON.parse(mod_fs.readFileSync('./typedef.json').toString());
+ parser = mod_ctype.parseCTF(data, { endian: 'big' });
+ mod_assert.deepEqual(parser.lstypes(), { 'bar_t': 'int',
+ 'int': 'int32_t' });
+}
+
+test();
--- /dev/null
+{ "metadata":
+ {
+ "ctf2json_version": "1.0",
+ "created_at": 1316302348,
+ "derived_from": "/lib/libc.so",
+ "ctf_version": 2,
+ "requested_types": [ "bar_t" ]
+ },
+"data":
+ [
+ { "name": "int", "integer": { "length": 4, "signed": true } },
+ { "name": "bar_t", "typedef": "int" }
+ ]
+}
--- /dev/null
+/*
+ * Battery of tests to break our floating point implementation. Oh ho ho.
+ *
+ * There are a few useful ways to generate the expected output. The first is
+ * just write a C program and write raw bytes out and inspect with xxd. Remember
+ * to consider whether or not you're on a big endian or little endian machine.
+ * Another useful site I found to help with some of this was:
+ *
+ * http://babbage.cs.qc.edu/IEEE-754/
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+function testfloat()
+{
+ var buffer = new Buffer(4);
+ /* Start off with some of the easy ones: +zero */
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+
+ ASSERT.equal(0, mod_ctype.rfloat(buffer, 'big', 0));
+ ASSERT.equal(0, mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* Test -0 */
+ buffer[0] = 0x80;
+ ASSERT.equal(0, mod_ctype.rfloat(buffer, 'big', 0));
+ buffer[3] = buffer[0];
+ buffer[0] = 0;
+ ASSERT.equal(0, mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* Catch +infin */
+ buffer[0] = 0x7f;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ ASSERT.equal(Number.POSITIVE_INFINITY,
+ mod_ctype.rfloat(buffer, 'big', 0));
+ buffer[3] = 0x7f;
+ buffer[2] = 0x80;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(Number.POSITIVE_INFINITY,
+ mod_ctype.rfloat(buffer, 'litle', 0));
+
+ /* Catch -infin */
+ buffer[0] = 0xff;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ ASSERT.equal(Number.NEGATIVE_INFINITY,
+ mod_ctype.rfloat(buffer, 'big', 0));
+ buffer[3] = 0xff;
+ buffer[2] = 0x80;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(Number.NEGATIVE_INFINITY,
+ mod_ctype.rfloat(buffer, 'litle', 0));
+
+ /* Catch NaN */
+
+ buffer[0] = 0x7f;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ buffer[3] = 0x23;
+ ASSERT.ok(isNaN(mod_ctype.rfloat(buffer, 'big', 0)));
+ buffer[3] = 0x7f;
+ buffer[2] = 0x80;
+ buffer[1] = 0x00;
+ buffer[0] = 0x23;
+ ASSERT.ok(isNaN(mod_ctype.rfloat(buffer, 'little', 0)));
+
+ /* Catch -infin */
+ buffer[0] = 0xff;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ buffer[3] = 0x23;
+ ASSERT.ok(isNaN(mod_ctype.rfloat(buffer, 'big', 0)));
+ buffer[3] = 0xff;
+ buffer[2] = 0x80;
+ buffer[1] = 0x00;
+ buffer[0] = 0x23;
+ ASSERT.ok(isNaN(mod_ctype.rfloat(buffer, 'little', 0)));
+
+ /* On to some basic tests */
+ /* 1.125 */
+ buffer[0] = 0x3f;
+ buffer[1] = 0x90;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ ASSERT.equal(1.125, mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x3f;
+ buffer[2] = 0x90;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(1.125, mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* ff34a2b0 -2.4010576103645774e+38 */
+ buffer[0] = 0xff;
+ buffer[1] = 0x34;
+ buffer[2] = 0xa2;
+ buffer[3] = 0xb0;
+ ASSERT.equal(-2.4010576103645774e+38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0xff;
+ buffer[2] = 0x34;
+ buffer[1] = 0xa2;
+ buffer[0] = 0xb0;
+ ASSERT.equal(-2.4010576103645774e+38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* Denormalized tests */
+
+ /* 0003f89a +/- 3.6468792534053364e-40 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x03;
+ buffer[2] = 0xf8;
+ buffer[3] = 0x9a;
+ ASSERT.equal(3.6468792534053364e-40,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x00;
+ buffer[2] = 0x03;
+ buffer[1] = 0xf8;
+ buffer[0] = 0x9a;
+ ASSERT.equal(3.6468792534053364e-40,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x03;
+ buffer[2] = 0xf8;
+ buffer[3] = 0x9a;
+ ASSERT.equal(-3.6468792534053364e-40,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x80;
+ buffer[2] = 0x03;
+ buffer[1] = 0xf8;
+ buffer[0] = 0x9a;
+ ASSERT.equal(-3.6468792534053364e-40,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+
+ /* Maximum and minimum normalized and denormalized values */
+
+ /* Largest normalized number +/- 3.4028234663852886e+38 */
+
+ buffer[0] = 0x7f;
+ buffer[1] = 0x7f;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ ASSERT.equal(3.4028234663852886e+38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x7f;
+ buffer[2] = 0x7f;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(3.4028234663852886e+38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ buffer[0] = 0xff;
+ buffer[1] = 0x7f;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ ASSERT.equal(-3.4028234663852886e+38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0xff;
+ buffer[2] = 0x7f;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(-3.4028234663852886e+38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* Smallest normalied number +/- 1.1754943508222875e-38 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ ASSERT.equal(1.1754943508222875e-38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x00;
+ buffer[2] = 0x80;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(1.1754943508222875e-38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ ASSERT.equal(-1.1754943508222875e-38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x80;
+ buffer[2] = 0x80;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(-1.1754943508222875e-38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+
+ /* Smallest denormalized number 1.401298464324817e-45 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x01;
+ ASSERT.equal(1.401298464324817e-45,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x00;
+ buffer[2] = 0x00;
+ buffer[1] = 0x00;
+ buffer[0] = 0x01;
+ ASSERT.equal(1.401298464324817e-45,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x01;
+ ASSERT.equal(-1.401298464324817e-45,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x80;
+ buffer[2] = 0x00;
+ buffer[1] = 0x00;
+ buffer[0] = 0x01;
+ ASSERT.equal(-1.401298464324817e-45,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* Largest denormalized value +/- 1.1754942106924411e-38 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x7f;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ ASSERT.equal(1.1754942106924411e-38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x00;
+ buffer[2] = 0x7f;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(1.1754942106924411e-38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x7f;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ ASSERT.equal(-1.1754942106924411e-38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+
+ buffer[3] = 0x80;
+ buffer[2] = 0x7f;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(-1.1754942106924411e-38,
+ mod_ctype.rfloat(buffer, 'little', 0));
+
+ /* Do some quick offset testing */
+ buffer = new Buffer(6);
+ buffer[0] = 0x7f;
+ buffer[1] = 0x4e;
+ buffer[2] = 0x8a;
+ buffer[3] = 0x79;
+ buffer[4] = 0xcd;
+ buffer[5] = 0x3f;
+
+ ASSERT.equal(2.745399582697325e+38,
+ mod_ctype.rfloat(buffer, 'big', 0));
+ ASSERT.equal(1161619072,
+ mod_ctype.rfloat(buffer, 'big', 1));
+ ASSERT.equal(-1.2027516403607578e-32,
+ mod_ctype.rfloat(buffer, 'big', 2));
+
+ ASSERT.equal(8.97661320504413e+34,
+ mod_ctype.rfloat(buffer, 'little', 0));
+ ASSERT.equal(-261661920,
+ mod_ctype.rfloat(buffer, 'little', 1));
+ ASSERT.equal(1.605271577835083,
+ mod_ctype.rfloat(buffer, 'little', 2));
+
+}
+
+function testdouble()
+{
+ var buffer = new Buffer(10);
+
+ /* Check 0 */
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+ ASSERT.equal(0,
+ mod_ctype.rdouble(buffer, 'big', 0));
+ ASSERT.equal(0,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+ ASSERT.equal(0,
+ mod_ctype.rdouble(buffer, 'big', 0));
+ buffer[7] = 0x80;
+ buffer[6] = 0;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 0;
+ ASSERT.equal(0,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Check NaN */
+ buffer[0] = 0x7f;
+ buffer[1] = 0xf0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 23;
+ ASSERT.ok(isNaN(mod_ctype.rdouble(buffer, 'big', 0)));
+
+ buffer[7] = 0x7f;
+ buffer[6] = 0xf0;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 23;
+ ASSERT.ok(isNaN(mod_ctype.rdouble(buffer, 'little', 0)));
+
+ buffer[0] = 0xff;
+ buffer[1] = 0xf0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 23;
+ ASSERT.ok(isNaN(mod_ctype.rdouble(buffer, 'big', 0)));
+
+ buffer[7] = 0xff;
+ buffer[6] = 0xf0;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 23;
+ ASSERT.ok(isNaN(mod_ctype.rdouble(buffer, 'little', 0)));
+
+ /* pos inf */
+ buffer[0] = 0x7f;
+ buffer[1] = 0xf0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+ ASSERT.equal(Number.POSITIVE_INFINITY,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x7f;
+ buffer[6] = 0xf0;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 0;
+ ASSERT.equal(Number.POSITIVE_INFINITY,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* neg inf */
+ buffer[0] = 0xff;
+ buffer[1] = 0xf0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+ ASSERT.equal(Number.NEGATIVE_INFINITY,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0xff;
+ buffer[6] = 0xf0;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 0;
+ ASSERT.equal(Number.NEGATIVE_INFINITY,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Simple normalized values */
+
+ /* +/- 1.125 */
+ buffer[0] = 0x3f;
+ buffer[1] = 0xf2;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+ ASSERT.equal(1.125,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x3f;
+ buffer[6] = 0xf2;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 0;
+ ASSERT.equal(1.125,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0xbf;
+ buffer[1] = 0xf2;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+ ASSERT.equal(-1.125,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0xbf;
+ buffer[6] = 0xf2;
+ buffer[5] = 0;
+ buffer[4] = 0;
+ buffer[3] = 0;
+ buffer[2] = 0;
+ buffer[1] = 0;
+ buffer[0] = 0;
+ ASSERT.equal(-1.125,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* +/- 1.4397318913736026e+283 */
+ buffer[0] = 0x7a;
+ buffer[1] = 0xb8;
+ buffer[2] = 0xc9;
+ buffer[3] = 0x34;
+ buffer[4] = 0x72;
+ buffer[5] = 0x16;
+ buffer[6] = 0xf9;
+ buffer[7] = 0x0e;
+ ASSERT.equal(1.4397318913736026e+283,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x7a;
+ buffer[6] = 0xb8;
+ buffer[5] = 0xc9;
+ buffer[4] = 0x34;
+ buffer[3] = 0x72;
+ buffer[2] = 0x16;
+ buffer[1] = 0xf9;
+ buffer[0] = 0x0e;
+ ASSERT.equal(1.4397318913736026e+283,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0xfa;
+ buffer[1] = 0xb8;
+ buffer[2] = 0xc9;
+ buffer[3] = 0x34;
+ buffer[4] = 0x72;
+ buffer[5] = 0x16;
+ buffer[6] = 0xf9;
+ buffer[7] = 0x0e;
+ ASSERT.equal(-1.4397318913736026e+283,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0xfa;
+ buffer[6] = 0xb8;
+ buffer[5] = 0xc9;
+ buffer[4] = 0x34;
+ buffer[3] = 0x72;
+ buffer[2] = 0x16;
+ buffer[1] = 0xf9;
+ buffer[0] = 0x0e;
+ ASSERT.equal(-1.4397318913736026e+283,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Denormalized values */
+ /* +/- 8.82521232268344e-309 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x06;
+ buffer[2] = 0x58;
+ buffer[3] = 0x94;
+ buffer[4] = 0x13;
+ buffer[5] = 0x27;
+ buffer[6] = 0x8a;
+ buffer[7] = 0xcd;
+ ASSERT.equal(8.82521232268344e-309,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x00;
+ buffer[6] = 0x06;
+ buffer[5] = 0x58;
+ buffer[4] = 0x94;
+ buffer[3] = 0x13;
+ buffer[2] = 0x27;
+ buffer[1] = 0x8a;
+ buffer[0] = 0xcd;
+ ASSERT.equal(8.82521232268344e-309,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x06;
+ buffer[2] = 0x58;
+ buffer[3] = 0x94;
+ buffer[4] = 0x13;
+ buffer[5] = 0x27;
+ buffer[6] = 0x8a;
+ buffer[7] = 0xcd;
+ ASSERT.equal(-8.82521232268344e-309,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x80;
+ buffer[6] = 0x06;
+ buffer[5] = 0x58;
+ buffer[4] = 0x94;
+ buffer[3] = 0x13;
+ buffer[2] = 0x27;
+ buffer[1] = 0x8a;
+ buffer[0] = 0xcd;
+ ASSERT.equal(-8.82521232268344e-309,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Edge cases, maximum and minimum values */
+
+ /* Smallest denormalized value 5e-324 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x01;
+ ASSERT.equal(5e-324,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x00;
+ buffer[6] = 0x00;
+ buffer[5] = 0x00;
+ buffer[4] = 0x00;
+ buffer[3] = 0x00;
+ buffer[2] = 0x00;
+ buffer[1] = 0x00;
+ buffer[0] = 0x01;
+ ASSERT.equal(5e-324,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x01;
+ ASSERT.equal(-5e-324,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x80;
+ buffer[6] = 0x00;
+ buffer[5] = 0x00;
+ buffer[4] = 0x00;
+ buffer[3] = 0x00;
+ buffer[2] = 0x00;
+ buffer[1] = 0x00;
+ buffer[0] = 0x01;
+ ASSERT.equal(-5e-324,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Largest denormalized value 2.225073858507201e-308 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x0f;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ buffer[4] = 0xff;
+ buffer[5] = 0xff;
+ buffer[6] = 0xff;
+ buffer[7] = 0xff;
+ ASSERT.equal(2.225073858507201e-308,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x00;
+ buffer[6] = 0x0f;
+ buffer[5] = 0xff;
+ buffer[4] = 0xff;
+ buffer[3] = 0xff;
+ buffer[2] = 0xff;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(2.225073858507201e-308,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x0f;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ buffer[4] = 0xff;
+ buffer[5] = 0xff;
+ buffer[6] = 0xff;
+ buffer[7] = 0xff;
+ ASSERT.equal(-2.225073858507201e-308,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x80;
+ buffer[6] = 0x0f;
+ buffer[5] = 0xff;
+ buffer[4] = 0xff;
+ buffer[3] = 0xff;
+ buffer[2] = 0xff;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(-2.225073858507201e-308,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Smallest normalized value 2.2250738585072014e-308 */
+ buffer[0] = 0x00;
+ buffer[1] = 0x10;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ ASSERT.equal(2.2250738585072014e-308,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x00;
+ buffer[6] = 0x10;
+ buffer[5] = 0x00;
+ buffer[4] = 0x00;
+ buffer[3] = 0x00;
+ buffer[2] = 0x00;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(2.2250738585072014e-308,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0x80;
+ buffer[1] = 0x10;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x00;
+ buffer[6] = 0x00;
+ buffer[7] = 0x00;
+ ASSERT.equal(-2.2250738585072014e-308,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x80;
+ buffer[6] = 0x10;
+ buffer[5] = 0x00;
+ buffer[4] = 0x00;
+ buffer[3] = 0x00;
+ buffer[2] = 0x00;
+ buffer[1] = 0x00;
+ buffer[0] = 0x00;
+ ASSERT.equal(-2.2250738585072014e-308,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Largest normalized value 1.7976931348623157e+308 */
+ buffer[0] = 0x7f;
+ buffer[1] = 0xef;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ buffer[4] = 0xff;
+ buffer[5] = 0xff;
+ buffer[6] = 0xff;
+ buffer[7] = 0xff;
+ ASSERT.equal(1.7976931348623157e+308,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0x7f;
+ buffer[6] = 0xef;
+ buffer[5] = 0xff;
+ buffer[4] = 0xff;
+ buffer[3] = 0xff;
+ buffer[2] = 0xff;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(1.7976931348623157e+308,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ buffer[0] = 0xff;
+ buffer[1] = 0xef;
+ buffer[2] = 0xff;
+ buffer[3] = 0xff;
+ buffer[4] = 0xff;
+ buffer[5] = 0xff;
+ buffer[6] = 0xff;
+ buffer[7] = 0xff;
+ ASSERT.equal(-1.7976931348623157e+308,
+ mod_ctype.rdouble(buffer, 'big', 0));
+
+ buffer[7] = 0xff;
+ buffer[6] = 0xef;
+ buffer[5] = 0xff;
+ buffer[4] = 0xff;
+ buffer[3] = 0xff;
+ buffer[2] = 0xff;
+ buffer[1] = 0xff;
+ buffer[0] = 0xff;
+ ASSERT.equal(-1.7976931348623157e+308,
+ mod_ctype.rdouble(buffer, 'little', 0));
+
+ /* Try offsets */
+ buffer[0] = 0xde;
+ buffer[1] = 0xad;
+ buffer[2] = 0xbe;
+ buffer[3] = 0xef;
+ buffer[4] = 0xba;
+ buffer[5] = 0xdd;
+ buffer[6] = 0xca;
+ buffer[7] = 0xfe;
+ buffer[8] = 0x16;
+ buffer[9] = 0x79;
+
+ ASSERT.equal(-1.1885958404126936e+148,
+ mod_ctype.rdouble(buffer, 'big', 0));
+ ASSERT.equal(-2.4299184080448593e-88,
+ mod_ctype.rdouble(buffer, 'big', 1));
+ ASSERT.equal(-0.000015130017658081283,
+ mod_ctype.rdouble(buffer, 'big', 2));
+
+ ASSERT.equal(-5.757458694845505e+302,
+ mod_ctype.rdouble(buffer, 'little', 0));
+ ASSERT.equal(6.436459604192476e-198,
+ mod_ctype.rdouble(buffer, 'little', 1));
+ ASSERT.equal(1.9903745632417286e+275,
+ mod_ctype.rdouble(buffer, 'little', 2));
+}
+
+testfloat();
+testdouble();
--- /dev/null
+/*
+ * Another place to find bugs that may yet plague us. This time with writing out
+ * floats to arrays. We are lazy and did basically just take the opposite of our
+ * test code to read in values.
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+
+/*
+ * A useful thing to keep around for debugging
+ * console.log('buffer[0]: ' + buffer[0].toString(16));
+ * console.log('buffer[1]: ' + buffer[1].toString(16));
+ * console.log('buffer[2]: ' + buffer[2].toString(16));
+ * console.log('buffer[3]: ' + buffer[3].toString(16));
+ * console.log('buffer[4]: ' + buffer[4].toString(16));
+ * console.log('buffer[5]: ' + buffer[5].toString(16));
+ * console.log('buffer[6]: ' + buffer[6].toString(16));
+ * console.log('buffer[7]: ' + buffer[7].toString(16));
+ */
+
+function testfloat()
+{
+ var buffer = new Buffer(4);
+ mod_ctype.wfloat(0, 'big', buffer, 0);
+ /* Start off with some of the easy ones: +zero */
+ ASSERT.equal(0, buffer[0]);
+ ASSERT.equal(0, buffer[1]);
+ ASSERT.equal(0, buffer[2]);
+ ASSERT.equal(0, buffer[3]);
+ mod_ctype.wfloat(0, 'little', buffer, 0);
+ ASSERT.equal(0, buffer[0]);
+ ASSERT.equal(0, buffer[1]);
+ ASSERT.equal(0, buffer[2]);
+ ASSERT.equal(0, buffer[3]);
+
+ /* Catch +infin */
+ mod_ctype.wfloat(Number.POSITIVE_INFINITY, 'big', buffer, 0);
+ ASSERT.equal(0x7f, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ mod_ctype.wfloat(Number.POSITIVE_INFINITY, 'little', buffer, 0);
+ ASSERT.equal(0x7f, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ /* Catch -infin */
+ mod_ctype.wfloat(Number.NEGATIVE_INFINITY, 'big', buffer, 0);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ mod_ctype.wfloat(Number.NEGATIVE_INFINITY, 'little', buffer, 0);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ /* Catch NaN */
+
+ /*
+ * NaN Is a litle weird in its requirements, so we're going to encode a
+ * bit of how we actually implement it into this test. Probably not the
+ * best, since technically the sign is a don't care and the mantissa
+ * needs to just be non-zero.
+ */
+ mod_ctype.wfloat(NaN, 'big', buffer, 0);
+ ASSERT.equal(0x7f, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x17, buffer[3]);
+ mod_ctype.wfloat(NaN, 'little', buffer, 0);
+ ASSERT.equal(0x7f, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x17, buffer[0]);
+
+ /* On to some basic tests */
+ /* 1.125 */
+ mod_ctype.wfloat(1.125, 'big', buffer, 0);
+ ASSERT.equal(0x3f, buffer[0]);
+ ASSERT.equal(0x90, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ mod_ctype.wfloat(1.125, 'little', buffer, 0);
+ ASSERT.equal(0x3f, buffer[3]);
+ ASSERT.equal(0x90, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ mod_ctype.wfloat(1.0000001192092896, 'big', buffer, 0);
+ ASSERT.equal(0x3f, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x01, buffer[3]);
+ mod_ctype.wfloat(1.0000001192092896, 'little', buffer, 0);
+ ASSERT.equal(0x3f, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+ mod_ctype.wfloat(1.0000001192092896, 'big', buffer, 0);
+ ASSERT.equal(0x3f, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x01, buffer[3]);
+ mod_ctype.wfloat(1.0000001192092896, 'little', buffer, 0);
+ ASSERT.equal(0x3f, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+ mod_ctype.wfloat(2.3283067140944524e-10, 'big', buffer, 0);
+ ASSERT.equal(0x2f, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x01, buffer[3]);
+ mod_ctype.wfloat(2.3283067140944524e-10, 'little', buffer, 0);
+ ASSERT.equal(0x2f, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+ /* ff34a2b0 -2.4010576103645774e+38 */
+ mod_ctype.wfloat(-2.4010576103645774e+38,
+ 'big', buffer, 0);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0x34, buffer[1]);
+ ASSERT.equal(0xa2, buffer[2]);
+ ASSERT.equal(0xb0, buffer[3]);
+ mod_ctype.wfloat(-2.4010576103645774e+38,
+ 'little', buffer, 0);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0x34, buffer[2]);
+ ASSERT.equal(0xa2, buffer[1]);
+ ASSERT.equal(0xb0, buffer[0]);
+
+ /* Denormalized tests */
+
+ /* 0003f89a +/- 3.6468792534053364e-40 */
+ mod_ctype.wfloat(3.6468792534053364e-40,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x03, buffer[1]);
+ ASSERT.equal(0xf8, buffer[2]);
+ ASSERT.equal(0x9a, buffer[3]);
+ mod_ctype.wfloat(3.6468792534053364e-40,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x03, buffer[2]);
+ ASSERT.equal(0xf8, buffer[1]);
+ ASSERT.equal(0x9a, buffer[0]);
+
+ mod_ctype.wfloat(-3.6468792534053364e-40,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x03, buffer[1]);
+ ASSERT.equal(0xf8, buffer[2]);
+ ASSERT.equal(0x9a, buffer[3]);
+ mod_ctype.wfloat(-3.6468792534053364e-40,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[3]);
+ ASSERT.equal(0x03, buffer[2]);
+ ASSERT.equal(0xf8, buffer[1]);
+ ASSERT.equal(0x9a, buffer[0]);
+
+ /* Maximum and minimum normalized and denormalized values */
+
+ /* Largest normalized number +/- 3.4028234663852886e+38 */
+
+ mod_ctype.wfloat(3.4028234663852886e+38,
+ 'big', buffer, 0);
+ ASSERT.equal(0x7f, buffer[0]);
+ ASSERT.equal(0x7f, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ mod_ctype.wfloat(3.4028234663852886e+38,
+ 'little', buffer, 0);
+ ASSERT.equal(0x7f, buffer[3]);
+ ASSERT.equal(0x7f, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+ mod_ctype.wfloat(-3.4028234663852886e+38,
+ 'big', buffer, 0);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0x7f, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ mod_ctype.wfloat(-3.4028234663852886e+38,
+ 'little', buffer, 0);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0x7f, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+ /* Smallest normalied number +/- 1.1754943508222875e-38 */
+
+ mod_ctype.wfloat(1.1754943508222875e-38,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ mod_ctype.wfloat(1.1754943508222875e-38,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ mod_ctype.wfloat(-1.1754943508222875e-38,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x80, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ mod_ctype.wfloat(-1.1754943508222875e-38,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[3]);
+ ASSERT.equal(0x80, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ /* Smallest denormalized number 1.401298464324817e-45 */
+ mod_ctype.wfloat(1.401298464324817e-45,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x01, buffer[3]);
+ mod_ctype.wfloat(1.401298464324817e-45,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+ mod_ctype.wfloat(-1.401298464324817e-45,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x01, buffer[3]);
+ mod_ctype.wfloat(-1.401298464324817e-45,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+ /* Largest denormalized value +/- 1.1754942106924411e-38 */
+
+ mod_ctype.wfloat(1.1754942106924411e-38,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x7f, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ mod_ctype.wfloat(1.1754942106924411e-38,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x7f, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+ mod_ctype.wfloat(-1.1754942106924411e-38,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x7f, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ mod_ctype.wfloat(-1.1754942106924411e-38,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[3]);
+ ASSERT.equal(0x7f, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+ /* Do some quick offset testing */
+ buffer = new Buffer(6);
+ mod_ctype.wfloat(-1.2027516403607578e-32,
+ 'big', buffer, 2);
+ ASSERT.equal(0x8a, buffer[2]);
+ ASSERT.equal(0x79, buffer[3]);
+ ASSERT.equal(0xcd, buffer[4]);
+ ASSERT.equal(0x3f, buffer[5]);
+
+ mod_ctype.wfloat(-1.2027516403607578e-32,
+ 'little', buffer, 2);
+ ASSERT.equal(0x8a, buffer[5]);
+ ASSERT.equal(0x79, buffer[4]);
+ ASSERT.equal(0xcd, buffer[3]);
+ ASSERT.equal(0x3f, buffer[2]);
+
+}
+
+function testdouble()
+{
+ var buffer = new Buffer(10);
+
+ /* Check 0 */
+ mod_ctype.wdouble(0, 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+ mod_ctype.wdouble(0, 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[7]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ /* Check NaN */
+ /* Similar to floats we are only generating a subset of values */
+ mod_ctype.wdouble(NaN, 'big', buffer, 0);
+ ASSERT.equal(0x7f, buffer[0]);
+ ASSERT.equal(0xf0, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x17, buffer[7]);
+ mod_ctype.wdouble(NaN, 'little', buffer, 0);
+ ASSERT.equal(0x7f, buffer[7]);
+ ASSERT.equal(0xf0, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x17, buffer[0]);
+
+ /* pos inf */
+ mod_ctype.wdouble(Number.POSITIVE_INFINITY,
+ 'big', buffer, 0);
+ ASSERT.equal(0x7f, buffer[0]);
+ ASSERT.equal(0xf0, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+ mod_ctype.wdouble(Number.POSITIVE_INFINITY,
+ 'little', buffer, 0);
+ ASSERT.equal(0x7f, buffer[7]);
+ ASSERT.equal(0xf0, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ /* neg inf */
+ mod_ctype.wdouble(Number.NEGATIVE_INFINITY,
+ 'big', buffer, 0);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0xf0, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+ mod_ctype.wdouble(Number.NEGATIVE_INFINITY,
+ 'little', buffer, 0);
+ ASSERT.equal(0xff, buffer[7]);
+ ASSERT.equal(0xf0, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ /* Simple normalized values */
+
+ /* +/- 1.125 */
+ mod_ctype.wdouble(1.125,
+ 'big', buffer, 0);
+ ASSERT.equal(0x3f, buffer[0]);
+ ASSERT.equal(0xf2, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+
+ mod_ctype.wdouble(1.125,
+ 'little', buffer, 0);
+ ASSERT.equal(0x3f, buffer[7]);
+ ASSERT.equal(0xf2, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ mod_ctype.wdouble(-1.125,
+ 'big', buffer, 0);
+ ASSERT.equal(0xbf, buffer[0]);
+ ASSERT.equal(0xf2, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+
+ mod_ctype.wdouble(-1.125,
+ 'little', buffer, 0);
+ ASSERT.equal(0xbf, buffer[7]);
+ ASSERT.equal(0xf2, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+
+ /* +/- 1.4397318913736026e+283 */
+ mod_ctype.wdouble(1.4397318913736026e+283,
+ 'big', buffer, 0);
+ ASSERT.equal(0x7a, buffer[0]);
+ ASSERT.equal(0xb8, buffer[1]);
+ ASSERT.equal(0xc9, buffer[2]);
+ ASSERT.equal(0x34, buffer[3]);
+ ASSERT.equal(0x72, buffer[4]);
+ ASSERT.equal(0x16, buffer[5]);
+ ASSERT.equal(0xf9, buffer[6]);
+ ASSERT.equal(0x0e, buffer[7]);
+
+ mod_ctype.wdouble(1.4397318913736026e+283,
+ 'little', buffer, 0);
+ ASSERT.equal(0x7a, buffer[7]);
+ ASSERT.equal(0xb8, buffer[6]);
+ ASSERT.equal(0xc9, buffer[5]);
+ ASSERT.equal(0x34, buffer[4]);
+ ASSERT.equal(0x72, buffer[3]);
+ ASSERT.equal(0x16, buffer[2]);
+ ASSERT.equal(0xf9, buffer[1]);
+ ASSERT.equal(0x0e, buffer[0]);
+
+ mod_ctype.wdouble(-1.4397318913736026e+283,
+ 'big', buffer, 0);
+ ASSERT.equal(0xfa, buffer[0]);
+ ASSERT.equal(0xb8, buffer[1]);
+ ASSERT.equal(0xc9, buffer[2]);
+ ASSERT.equal(0x34, buffer[3]);
+ ASSERT.equal(0x72, buffer[4]);
+ ASSERT.equal(0x16, buffer[5]);
+ ASSERT.equal(0xf9, buffer[6]);
+ ASSERT.equal(0x0e, buffer[7]);
+
+ mod_ctype.wdouble(-1.4397318913736026e+283,
+ 'little', buffer, 0);
+ ASSERT.equal(0xfa, buffer[7]);
+ ASSERT.equal(0xb8, buffer[6]);
+ ASSERT.equal(0xc9, buffer[5]);
+ ASSERT.equal(0x34, buffer[4]);
+ ASSERT.equal(0x72, buffer[3]);
+ ASSERT.equal(0x16, buffer[2]);
+ ASSERT.equal(0xf9, buffer[1]);
+ ASSERT.equal(0x0e, buffer[0]);
+
+ /* Denormalized values */
+ /* +/- 8.82521232268344e-309 */
+ mod_ctype.wdouble(8.82521232268344e-309,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x06, buffer[1]);
+ ASSERT.equal(0x58, buffer[2]);
+ ASSERT.equal(0x94, buffer[3]);
+ ASSERT.equal(0x13, buffer[4]);
+ ASSERT.equal(0x27, buffer[5]);
+ ASSERT.equal(0x8a, buffer[6]);
+ ASSERT.equal(0xcd, buffer[7]);
+
+ mod_ctype.wdouble(8.82521232268344e-309,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[7]);
+ ASSERT.equal(0x06, buffer[6]);
+ ASSERT.equal(0x58, buffer[5]);
+ ASSERT.equal(0x94, buffer[4]);
+ ASSERT.equal(0x13, buffer[3]);
+ ASSERT.equal(0x27, buffer[2]);
+ ASSERT.equal(0x8a, buffer[1]);
+ ASSERT.equal(0xcd, buffer[0]);
+
+ mod_ctype.wdouble(-8.82521232268344e-309,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x06, buffer[1]);
+ ASSERT.equal(0x58, buffer[2]);
+ ASSERT.equal(0x94, buffer[3]);
+ ASSERT.equal(0x13, buffer[4]);
+ ASSERT.equal(0x27, buffer[5]);
+ ASSERT.equal(0x8a, buffer[6]);
+ ASSERT.equal(0xcd, buffer[7]);
+
+ mod_ctype.wdouble(-8.82521232268344e-309,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[7]);
+ ASSERT.equal(0x06, buffer[6]);
+ ASSERT.equal(0x58, buffer[5]);
+ ASSERT.equal(0x94, buffer[4]);
+ ASSERT.equal(0x13, buffer[3]);
+ ASSERT.equal(0x27, buffer[2]);
+ ASSERT.equal(0x8a, buffer[1]);
+ ASSERT.equal(0xcd, buffer[0]);
+
+
+ /* Edge cases, maximum and minimum values */
+
+ /* Smallest denormalized value 5e-324 */
+ mod_ctype.wdouble(5e-324,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x01, buffer[7]);
+
+ mod_ctype.wdouble(5e-324,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[7]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+ mod_ctype.wdouble(-5e-324,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x01, buffer[7]);
+
+ mod_ctype.wdouble(-5e-324,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[7]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x01, buffer[0]);
+
+
+
+ /* Largest denormalized value 2.225073858507201e-308 */
+ mod_ctype.wdouble(2.225073858507201e-308,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x0f, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[6]);
+ ASSERT.equal(0xff, buffer[7]);
+
+ mod_ctype.wdouble(2.225073858507201e-308,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[7]);
+ ASSERT.equal(0x0f, buffer[6]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+ mod_ctype.wdouble(-2.225073858507201e-308,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x0f, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[6]);
+ ASSERT.equal(0xff, buffer[7]);
+
+ mod_ctype.wdouble(-2.225073858507201e-308,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[7]);
+ ASSERT.equal(0x0f, buffer[6]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+
+ /* Smallest normalized value 2.2250738585072014e-308 */
+ mod_ctype.wdouble(2.2250738585072014e-308,
+ 'big', buffer, 0);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x10, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+
+ mod_ctype.wdouble(2.2250738585072014e-308,
+ 'little', buffer, 0);
+ ASSERT.equal(0x00, buffer[7]);
+ ASSERT.equal(0x10, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+ mod_ctype.wdouble(-2.2250738585072014e-308,
+ 'big', buffer, 0);
+ ASSERT.equal(0x80, buffer[0]);
+ ASSERT.equal(0x10, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+
+ mod_ctype.wdouble(-2.2250738585072014e-308,
+ 'little', buffer, 0);
+ ASSERT.equal(0x80, buffer[7]);
+ ASSERT.equal(0x10, buffer[6]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[4]);
+ ASSERT.equal(0x00, buffer[3]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[0]);
+
+
+ /* Largest normalized value 1.7976931348623157e+308 */
+ mod_ctype.wdouble(1.7976931348623157e+308,
+ 'big', buffer, 0);
+ ASSERT.equal(0x7f, buffer[0]);
+ ASSERT.equal(0xef, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[6]);
+ ASSERT.equal(0xff, buffer[7]);
+
+ mod_ctype.wdouble(1.7976931348623157e+308,
+ 'little', buffer, 0);
+ ASSERT.equal(0x7f, buffer[7]);
+ ASSERT.equal(0xef, buffer[6]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+ mod_ctype.wdouble(-1.7976931348623157e+308,
+ 'big', buffer, 0);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0xef, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[6]);
+ ASSERT.equal(0xff, buffer[7]);
+
+ mod_ctype.wdouble(-1.7976931348623157e+308,
+ 'little', buffer, 0);
+ ASSERT.equal(0xff, buffer[7]);
+ ASSERT.equal(0xef, buffer[6]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[4]);
+ ASSERT.equal(0xff, buffer[3]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[0]);
+
+
+ /* Try offsets */
+ buffer[0] = 0xde;
+ buffer[1] = 0xad;
+ buffer[2] = 0xbe;
+ buffer[3] = 0xef;
+ buffer[4] = 0xba;
+ buffer[5] = 0xdd;
+ buffer[6] = 0xca;
+ buffer[7] = 0xfe;
+ buffer[8] = 0x16;
+ buffer[9] = 0x79;
+
+ mod_ctype.wdouble(-0.000015130017658081283,
+ 'big', buffer, 2);
+ ASSERT.equal(0xbe, buffer[2]);
+ ASSERT.equal(0xef, buffer[3]);
+ ASSERT.equal(0xba, buffer[4]);
+ ASSERT.equal(0xdd, buffer[5]);
+ ASSERT.equal(0xca, buffer[6]);
+ ASSERT.equal(0xfe, buffer[7]);
+ ASSERT.equal(0x16, buffer[8]);
+ ASSERT.equal(0x79, buffer[9]);
+
+ mod_ctype.wdouble(-0.000015130017658081283,
+ 'little', buffer, 2);
+ ASSERT.equal(0xbe, buffer[9]);
+ ASSERT.equal(0xef, buffer[8]);
+ ASSERT.equal(0xba, buffer[7]);
+ ASSERT.equal(0xdd, buffer[6]);
+ ASSERT.equal(0xca, buffer[5]);
+ ASSERT.equal(0xfe, buffer[4]);
+ ASSERT.equal(0x16, buffer[3]);
+ ASSERT.equal(0x79, buffer[2]);
+}
+
+testfloat();
+testdouble();
--- /dev/null
+/*
+ * Test our ability to read and write signed 64-bit integers.
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+function testRead()
+{
+ var res, data;
+ data = new Buffer(10);
+
+ data[0] = 0x32;
+ data[1] = 0x65;
+ data[2] = 0x42;
+ data[3] = 0x56;
+ data[4] = 0x23;
+ data[5] = 0xff;
+ data[6] = 0xff;
+ data[7] = 0xff;
+ data[8] = 0x89;
+ data[9] = 0x11;
+ res = mod_ctype.rsint64(data, 'big', 0);
+ ASSERT.equal(0x32654256, res[0]);
+ ASSERT.equal(0x23ffffff, res[1]);
+ res = mod_ctype.rsint64(data, 'big', 1);
+ ASSERT.equal(0x65425623, res[0]);
+ ASSERT.equal(0xffffff89, res[1]);
+ res = mod_ctype.rsint64(data, 'big', 2);
+ ASSERT.equal(0x425623ff, res[0]);
+ ASSERT.equal(0xffff8911, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 0);
+ ASSERT.equal(-0x000000dc, res[0]);
+ ASSERT.equal(-0xa9bd9ace, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 1);
+ ASSERT.equal(-0x76000000, res[0]);
+ ASSERT.equal(-0xdca9bd9b, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 2);
+ ASSERT.equal(0x1189ffff, res[0]);
+ ASSERT.equal(0xff235642, res[1]);
+
+ data.fill(0x00);
+ res = mod_ctype.rsint64(data, 'big', 0);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(0x00000000, res[1]);
+ res = mod_ctype.rsint64(data, 'big', 1);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(0x00000000, res[1]);
+ res = mod_ctype.rsint64(data, 'big', 2);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(0x00000000, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 0);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(0x00000000, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 1);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(0x00000000, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 2);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(0x00000000, res[1]);
+
+ data.fill(0xff);
+ res = mod_ctype.rsint64(data, 'big', 0);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(-1, res[1]);
+ res = mod_ctype.rsint64(data, 'big', 1);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(-1, res[1]);
+ res = mod_ctype.rsint64(data, 'big', 2);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(-1, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 0);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(-1, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 1);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(-1, res[1]);
+ res = mod_ctype.rsint64(data, 'little', 2);
+ ASSERT.equal(0x00000000, res[0]);
+ ASSERT.equal(-1, res[1]);
+
+ data[0] = 0x80;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = 0x00;
+ data[5] = 0x00;
+ data[6] = 0x00;
+ data[7] = 0x00;
+ res = mod_ctype.rsint64(data, 'big', 0);
+ ASSERT.equal(-0x80000000, res[0]);
+ ASSERT.equal(0, res[1]);
+
+
+ data[7] = 0x80;
+ data[6] = 0x00;
+ data[5] = 0x00;
+ data[4] = 0x00;
+ data[3] = 0x00;
+ data[2] = 0x00;
+ data[1] = 0x00;
+ data[0] = 0x00;
+ res = mod_ctype.rsint64(data, 'little', 0);
+ ASSERT.equal(-0x80000000, res[0]);
+ ASSERT.equal(0, res[1]);
+
+ data[0] = 0x80;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = 0x00;
+ data[5] = 0x00;
+ data[6] = 0x00;
+ data[7] = 0x01;
+ res = mod_ctype.rsint64(data, 'big', 0);
+ ASSERT.equal(-0x7fffffff, res[0]);
+ ASSERT.equal(-0xffffffff, res[1]);
+
+
+}
+
+function testWriteZero()
+{
+ var data, buf;
+ buf = new Buffer(10);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ ASSERT.equal(0, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wsint64(data, 'big', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0, buf[9]);
+
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ ASSERT.equal(0, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wsint64(data, 'little', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0, buf[9]);
+}
+
+/*
+ * Also include tests that are going to force us to go into a negative value and
+ * insure that it's written correctly.
+ */
+function testWrite()
+{
+ var data, buf;
+
+ buf = new Buffer(10);
+ data = [ 0x234456, 0x87 ];
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ ASSERT.equal(0x00, buf[0]);
+ ASSERT.equal(0x23, buf[1]);
+ ASSERT.equal(0x44, buf[2]);
+ ASSERT.equal(0x56, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x87, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x23, buf[2]);
+ ASSERT.equal(0x44, buf[3]);
+ ASSERT.equal(0x56, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x87, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x23, buf[3]);
+ ASSERT.equal(0x44, buf[4]);
+ ASSERT.equal(0x56, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x00, buf[8]);
+ ASSERT.equal(0x87, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ ASSERT.equal(0x87, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x56, buf[4]);
+ ASSERT.equal(0x44, buf[5]);
+ ASSERT.equal(0x23, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x87, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x56, buf[5]);
+ ASSERT.equal(0x44, buf[6]);
+ ASSERT.equal(0x23, buf[7]);
+ ASSERT.equal(0x00, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0x87, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x56, buf[6]);
+ ASSERT.equal(0x44, buf[7]);
+ ASSERT.equal(0x23, buf[8]);
+ ASSERT.equal(0x00, buf[9]);
+
+ data = [0x3421, 0x34abcdba];
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ ASSERT.equal(0x00, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x34, buf[2]);
+ ASSERT.equal(0x21, buf[3]);
+ ASSERT.equal(0x34, buf[4]);
+ ASSERT.equal(0xab, buf[5]);
+ ASSERT.equal(0xcd, buf[6]);
+ ASSERT.equal(0xba, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x34, buf[3]);
+ ASSERT.equal(0x21, buf[4]);
+ ASSERT.equal(0x34, buf[5]);
+ ASSERT.equal(0xab, buf[6]);
+ ASSERT.equal(0xcd, buf[7]);
+ ASSERT.equal(0xba, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x34, buf[4]);
+ ASSERT.equal(0x21, buf[5]);
+ ASSERT.equal(0x34, buf[6]);
+ ASSERT.equal(0xab, buf[7]);
+ ASSERT.equal(0xcd, buf[8]);
+ ASSERT.equal(0xba, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ ASSERT.equal(0xba, buf[0]);
+ ASSERT.equal(0xcd, buf[1]);
+ ASSERT.equal(0xab, buf[2]);
+ ASSERT.equal(0x34, buf[3]);
+ ASSERT.equal(0x21, buf[4]);
+ ASSERT.equal(0x34, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0xba, buf[1]);
+ ASSERT.equal(0xcd, buf[2]);
+ ASSERT.equal(0xab, buf[3]);
+ ASSERT.equal(0x34, buf[4]);
+ ASSERT.equal(0x21, buf[5]);
+ ASSERT.equal(0x34, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x00, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0xba, buf[2]);
+ ASSERT.equal(0xcd, buf[3]);
+ ASSERT.equal(0xab, buf[4]);
+ ASSERT.equal(0x34, buf[5]);
+ ASSERT.equal(0x21, buf[6]);
+ ASSERT.equal(0x34, buf[7]);
+ ASSERT.equal(0x00, buf[8]);
+ ASSERT.equal(0x00, buf[9]);
+
+
+ data = [ -0x80000000, 0 ];
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ ASSERT.equal(0x80, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ ASSERT.equal(0x00, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x80, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ data = [ -0x7fffffff, -0xffffffff ];
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ ASSERT.equal(0x80, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x01, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ ASSERT.equal(0x01, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x80, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ data = [ 0x0, -0x1];
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ ASSERT.equal(0xff, buf[0]);
+ ASSERT.equal(0xff, buf[1]);
+ ASSERT.equal(0xff, buf[2]);
+ ASSERT.equal(0xff, buf[3]);
+ ASSERT.equal(0xff, buf[4]);
+ ASSERT.equal(0xff, buf[5]);
+ ASSERT.equal(0xff, buf[6]);
+ ASSERT.equal(0xff, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ ASSERT.equal(0xff, buf[0]);
+ ASSERT.equal(0xff, buf[1]);
+ ASSERT.equal(0xff, buf[2]);
+ ASSERT.equal(0xff, buf[3]);
+ ASSERT.equal(0xff, buf[4]);
+ ASSERT.equal(0xff, buf[5]);
+ ASSERT.equal(0xff, buf[6]);
+ ASSERT.equal(0xff, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+}
+
+/*
+ * Make sure we catch invalid writes.
+ */
+function testWriteInvalid()
+{
+ var data, buf;
+
+ /* Buffer too small */
+ buf = new Buffer(4);
+ data = [ 0, 0];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 0);
+ }, Error, 'buffer too small');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 0);
+ }, Error, 'buffer too small');
+
+ /* Beyond the end of the buffer */
+ buf = new Buffer(12);
+ data = [ 0, 0];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 11);
+ }, Error, 'write beyond end of buffer');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 11);
+ }, Error, 'write beyond end of buffer');
+
+ /* Write fractional values */
+ buf = new Buffer(12);
+ data = [ 3.33, 0 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ 0, 3.3 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ -3.33, 0 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ 0, -3.3 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ 3.33, 2.42 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ 3.33, -2.42 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ -3.33, -2.42 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ -3.33, 2.42 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ /* Signs don't match */
+ buf = new Buffer(12);
+ data = [ 0x800000, -0x32 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ -0x800000, 0x32 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ /* Write values that are too large */
+ buf = new Buffer(12);
+ data = [ 0x80000000, 0 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ 0x7fffffff, 0x100000000 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ 0x00, 0x800000000 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ 0xffffffffff, 0xffffff238 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ 0x23, 0xffffff238 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ -0x80000000, -0xfff238 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ -0x80000004, -0xfff238 ];
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wsint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+}
+
+
+testRead();
+testWrite();
+testWriteZero();
+testWriteInvalid();
--- /dev/null
+/*
+ * Tests to verify we're reading in signed integers correctly
+ */
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+/*
+ * Test 8 bit signed integers
+ */
+function test8()
+{
+ var data = new Buffer(4);
+
+ data[0] = 0x23;
+ ASSERT.equal(0x23, mod_ctype.rsint8(data, 'big', 0));
+ ASSERT.equal(0x23, mod_ctype.rsint8(data, 'little', 0));
+
+ data[0] = 0xff;
+ ASSERT.equal(-1, mod_ctype.rsint8(data, 'big', 0));
+ ASSERT.equal(-1, mod_ctype.rsint8(data, 'little', 0));
+
+ data[0] = 0x87;
+ data[1] = 0xab;
+ data[2] = 0x7c;
+ data[3] = 0xef;
+ ASSERT.equal(-121, mod_ctype.rsint8(data, 'big', 0));
+ ASSERT.equal(-85, mod_ctype.rsint8(data, 'big', 1));
+ ASSERT.equal(124, mod_ctype.rsint8(data, 'big', 2));
+ ASSERT.equal(-17, mod_ctype.rsint8(data, 'big', 3));
+ ASSERT.equal(-121, mod_ctype.rsint8(data, 'little', 0));
+ ASSERT.equal(-85, mod_ctype.rsint8(data, 'little', 1));
+ ASSERT.equal(124, mod_ctype.rsint8(data, 'little', 2));
+ ASSERT.equal(-17, mod_ctype.rsint8(data, 'little', 3));
+}
+
+function test16()
+{
+ var buffer = new Buffer(6);
+ buffer[0] = 0x16;
+ buffer[1] = 0x79;
+ ASSERT.equal(0x1679, mod_ctype.rsint16(buffer, 'big', 0));
+ ASSERT.equal(0x7916, mod_ctype.rsint16(buffer, 'little', 0));
+
+ buffer[0] = 0xff;
+ buffer[1] = 0x80;
+ ASSERT.equal(-128, mod_ctype.rsint16(buffer, 'big', 0));
+ ASSERT.equal(-32513, mod_ctype.rsint16(buffer, 'little', 0));
+
+ /* test offset with weenix */
+ buffer[0] = 0x77;
+ buffer[1] = 0x65;
+ buffer[2] = 0x65;
+ buffer[3] = 0x6e;
+ buffer[4] = 0x69;
+ buffer[5] = 0x78;
+ ASSERT.equal(0x7765, mod_ctype.rsint16(buffer, 'big', 0));
+ ASSERT.equal(0x6565, mod_ctype.rsint16(buffer, 'big', 1));
+ ASSERT.equal(0x656e, mod_ctype.rsint16(buffer, 'big', 2));
+ ASSERT.equal(0x6e69, mod_ctype.rsint16(buffer, 'big', 3));
+ ASSERT.equal(0x6978, mod_ctype.rsint16(buffer, 'big', 4));
+ ASSERT.equal(0x6577, mod_ctype.rsint16(buffer, 'little', 0));
+ ASSERT.equal(0x6565, mod_ctype.rsint16(buffer, 'little', 1));
+ ASSERT.equal(0x6e65, mod_ctype.rsint16(buffer, 'little', 2));
+ ASSERT.equal(0x696e, mod_ctype.rsint16(buffer, 'little', 3));
+ ASSERT.equal(0x7869, mod_ctype.rsint16(buffer, 'little', 4));
+}
+
+function test32()
+{
+ var buffer = new Buffer(6);
+ buffer[0] = 0x43;
+ buffer[1] = 0x53;
+ buffer[2] = 0x16;
+ buffer[3] = 0x79;
+ ASSERT.equal(0x43531679, mod_ctype.rsint32(buffer, 'big', 0));
+ ASSERT.equal(0x79165343, mod_ctype.rsint32(buffer, 'little', 0));
+
+ buffer[0] = 0xff;
+ buffer[1] = 0xfe;
+ buffer[2] = 0xef;
+ buffer[3] = 0xfa;
+ ASSERT.equal(-69638, mod_ctype.rsint32(buffer, 'big', 0));
+ ASSERT.equal(-84934913, mod_ctype.rsint32(buffer, 'little', 0));
+
+ buffer[0] = 0x42;
+ buffer[1] = 0xc3;
+ buffer[2] = 0x95;
+ buffer[3] = 0xa9;
+ buffer[4] = 0x36;
+ buffer[5] = 0x17;
+ ASSERT.equal(0x42c395a9, mod_ctype.rsint32(buffer, 'big', 0));
+ ASSERT.equal(-1013601994, mod_ctype.rsint32(buffer, 'big', 1));
+ ASSERT.equal(-1784072681, mod_ctype.rsint32(buffer, 'big', 2));
+ ASSERT.equal(-1449802942, mod_ctype.rsint32(buffer, 'little', 0));
+ ASSERT.equal(917083587, mod_ctype.rsint32(buffer, 'little', 1));
+ ASSERT.equal(389458325, mod_ctype.rsint32(buffer, 'little', 2));
+}
+
+test8();
+test16();
+test32();
--- /dev/null
+/*
+ * Test to make sure that we properly are erroring whenever we try to write
+ * beyond the size of the integer.
+ */
+
+var mod_ctio = require('../../../ctio.js');
+var mod_assert = require('assert');
+var tb = new Buffer(16); /* Largest buffer we'll need */
+
+var cases = [
+ { func:
+ function () {
+ mod_ctio.wsint8(0x80, 'big', tb, 0);
+ }, test: '+int8_t' },
+ { func:
+ function () {
+ mod_ctio.wsint8(-0x81, 'big', tb, 0);
+ }, test: '-int8_t' },
+
+ { func:
+ function () {
+ mod_ctio.wsint16(0x8000, 'big', tb, 0);
+ }, test: '+int16_t' },
+ { func:
+ function () {
+ mod_ctio.wsint16(-0x8001, 'big', tb, 0);
+ }, test: '-int16_t' },
+ { func:
+ function () {
+ mod_ctio.wsint32(0x80000000, 'big', tb, 0);
+ }, test: '+int32_t' },
+ { func:
+ function () {
+ mod_ctio.wsint32(-0x80000001, 'big', tb, 0);
+ }, test: '-int32_t' },
+ { func:
+ function () {
+ mod_ctio.wsint64([ 0x80000000, 0 ], 'big', tb, 0);
+ }, test: '+int64_t' },
+ { func:
+ function () {
+ mod_ctio.wsint64([ -0x80000000, -1 ], 'big', tb, 0);
+ }, test: '-int64_t' }
+];
+
+function test()
+{
+ var ii;
+ for (ii = 0; ii < cases.length; ii++)
+ mod_assert.throws(cases[ii]['func'], Error, cases[ii]['test']);
+}
+
+test();
--- /dev/null
+/*
+ * Tests to verify we're writing signed integers correctly
+ */
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+function test8()
+{
+ var buffer = new Buffer(4);
+ mod_ctype.wsint8(0x23, 'big', buffer, 0);
+ mod_ctype.wsint8(0x23, 'little', buffer, 1);
+ mod_ctype.wsint8(-5, 'big', buffer, 2);
+ mod_ctype.wsint8(-5, 'little', buffer, 3);
+
+ ASSERT.equal(0x23, buffer[0]);
+ ASSERT.equal(0x23, buffer[1]);
+ ASSERT.equal(0xfb, buffer[2]);
+ ASSERT.equal(0xfb, buffer[3]);
+
+ /* Make sure we handle truncation correctly */
+ ASSERT.throws(function () {
+ mod_ctype.wsint8(0xabc, 'big', buffer, 0);
+ });
+ ASSERT.throws(function () {
+ mod_ctype.wsint8(0xabc, 'little', buffer, 0);
+ });
+}
+
+function test16()
+{
+ var buffer = new Buffer(6);
+ mod_ctype.wsint16(0x0023, 'big', buffer, 0);
+ mod_ctype.wsint16(0x0023, 'little', buffer, 2);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x23, buffer[1]);
+ ASSERT.equal(0x23, buffer[2]);
+ ASSERT.equal(0x00, buffer[3]);
+ mod_ctype.wsint16(-5, 'big', buffer, 0);
+ mod_ctype.wsint16(-5, 'little', buffer, 2);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0xfb, buffer[1]);
+ ASSERT.equal(0xfb, buffer[2]);
+ ASSERT.equal(0xff, buffer[3]);
+
+ mod_ctype.wsint16(-1679, 'big', buffer, 1);
+ mod_ctype.wsint16(-1679, 'little', buffer, 3);
+ ASSERT.equal(0xf9, buffer[1]);
+ ASSERT.equal(0x71, buffer[2]);
+ ASSERT.equal(0x71, buffer[3]);
+ ASSERT.equal(0xf9, buffer[4]);
+}
+
+function test32()
+{
+ var buffer = new Buffer(8);
+ mod_ctype.wsint32(0x23, 'big', buffer, 0);
+ mod_ctype.wsint32(0x23, 'little', buffer, 4);
+ ASSERT.equal(0x00, buffer[0]);
+ ASSERT.equal(0x00, buffer[1]);
+ ASSERT.equal(0x00, buffer[2]);
+ ASSERT.equal(0x23, buffer[3]);
+ ASSERT.equal(0x23, buffer[4]);
+ ASSERT.equal(0x00, buffer[5]);
+ ASSERT.equal(0x00, buffer[6]);
+ ASSERT.equal(0x00, buffer[7]);
+
+ mod_ctype.wsint32(-5, 'big', buffer, 0);
+ mod_ctype.wsint32(-5, 'little', buffer, 4);
+ ASSERT.equal(0xff, buffer[0]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xff, buffer[2]);
+ ASSERT.equal(0xfb, buffer[3]);
+ ASSERT.equal(0xfb, buffer[4]);
+ ASSERT.equal(0xff, buffer[5]);
+ ASSERT.equal(0xff, buffer[6]);
+ ASSERT.equal(0xff, buffer[7]);
+
+ mod_ctype.wsint32(-805306713, 'big', buffer, 0);
+ mod_ctype.wsint32(-805306713, 'litle', buffer, 4);
+ ASSERT.equal(0xcf, buffer[0]);
+ ASSERT.equal(0xff, buffer[1]);
+ ASSERT.equal(0xfe, buffer[2]);
+ ASSERT.equal(0xa7, buffer[3]);
+ ASSERT.equal(0xa7, buffer[4]);
+ ASSERT.equal(0xfe, buffer[5]);
+ ASSERT.equal(0xff, buffer[6]);
+ ASSERT.equal(0xcf, buffer[7]);
+}
+
+test8();
+test16();
+test32();
--- /dev/null
+/*
+ * Test our ability to read and write unsigned 64-bit integers.
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+function testRead()
+{
+ var res, data;
+ data = new Buffer(10);
+
+ data[0] = 0x32;
+ data[1] = 0x65;
+ data[2] = 0x42;
+ data[3] = 0x56;
+ data[4] = 0x23;
+ data[5] = 0xff;
+ data[6] = 0xff;
+ data[7] = 0xff;
+ data[8] = 0x89;
+ data[9] = 0x11;
+ res = mod_ctype.ruint64(data, 'big', 0);
+ ASSERT.equal(0x32654256, res[0]);
+ ASSERT.equal(0x23ffffff, res[1]);
+ res = mod_ctype.ruint64(data, 'big', 1);
+ ASSERT.equal(0x65425623, res[0]);
+ ASSERT.equal(0xffffff89, res[1]);
+ res = mod_ctype.ruint64(data, 'big', 2);
+ ASSERT.equal(0x425623ff, res[0]);
+ ASSERT.equal(0xffff8911, res[1]);
+ res = mod_ctype.ruint64(data, 'little', 0);
+ ASSERT.equal(0xffffff23, res[0]);
+ ASSERT.equal(0x56426532, res[1]);
+ res = mod_ctype.ruint64(data, 'little', 1);
+ ASSERT.equal(0x89ffffff, res[0]);
+ ASSERT.equal(0x23564265, res[1]);
+ res = mod_ctype.ruint64(data, 'little', 2);
+ ASSERT.equal(0x1189ffff, res[0]);
+ ASSERT.equal(0xff235642, res[1]);
+
+}
+
+function testReadOver()
+{
+ var res, data;
+ data = new Buffer(10);
+
+ data[0] = 0x80;
+ data[1] = 0xff;
+ data[2] = 0x80;
+ data[3] = 0xff;
+ data[4] = 0x80;
+ data[5] = 0xff;
+ data[6] = 0x80;
+ data[7] = 0xff;
+ data[8] = 0x80;
+ data[9] = 0xff;
+ res = mod_ctype.ruint64(data, 'big', 0);
+ ASSERT.equal(0x80ff80ff, res[0]);
+ ASSERT.equal(0x80ff80ff, res[1]);
+ res = mod_ctype.ruint64(data, 'big', 1);
+ ASSERT.equal(0xff80ff80, res[0]);
+ ASSERT.equal(0xff80ff80, res[1]);
+ res = mod_ctype.ruint64(data, 'big', 2);
+ ASSERT.equal(0x80ff80ff, res[0]);
+ ASSERT.equal(0x80ff80ff, res[1]);
+ res = mod_ctype.ruint64(data, 'little', 0);
+ ASSERT.equal(0xff80ff80, res[0]);
+ ASSERT.equal(0xff80ff80, res[1]);
+ res = mod_ctype.ruint64(data, 'little', 1);
+ ASSERT.equal(0x80ff80ff, res[0]);
+ ASSERT.equal(0x80ff80ff, res[1]);
+ res = mod_ctype.ruint64(data, 'little', 2);
+ ASSERT.equal(0xff80ff80, res[0]);
+ ASSERT.equal(0xff80ff80, res[1]);
+}
+
+function testWriteZero()
+{
+ var data, buf;
+ buf = new Buffer(10);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wuint64(data, 'big', buf, 0);
+ ASSERT.equal(0, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wuint64(data, 'big', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0, buf[9]);
+
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wuint64(data, 'little', buf, 0);
+ ASSERT.equal(0, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ data = [0, 0];
+ mod_ctype.wuint64(data, 'little', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0, buf[2]);
+ ASSERT.equal(0, buf[3]);
+ ASSERT.equal(0, buf[4]);
+ ASSERT.equal(0, buf[5]);
+ ASSERT.equal(0, buf[6]);
+ ASSERT.equal(0, buf[7]);
+ ASSERT.equal(0, buf[8]);
+ ASSERT.equal(0, buf[9]);
+}
+
+/*
+ * Also include tests that are going to force us to go into a negative value and
+ * insure that it's written correctly.
+ */
+function testWrite()
+{
+ var data, buf;
+
+ buf = new Buffer(10);
+ data = [ 0x234456, 0x87 ];
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'big', buf, 0);
+ ASSERT.equal(0x00, buf[0]);
+ ASSERT.equal(0x23, buf[1]);
+ ASSERT.equal(0x44, buf[2]);
+ ASSERT.equal(0x56, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x87, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x23, buf[2]);
+ ASSERT.equal(0x44, buf[3]);
+ ASSERT.equal(0x56, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x87, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'big', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x23, buf[3]);
+ ASSERT.equal(0x44, buf[4]);
+ ASSERT.equal(0x56, buf[5]);
+ ASSERT.equal(0x00, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x00, buf[8]);
+ ASSERT.equal(0x87, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'little', buf, 0);
+ ASSERT.equal(0x87, buf[0]);
+ ASSERT.equal(0x00, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x56, buf[4]);
+ ASSERT.equal(0x44, buf[5]);
+ ASSERT.equal(0x23, buf[6]);
+ ASSERT.equal(0x00, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x87, buf[1]);
+ ASSERT.equal(0x00, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x56, buf[5]);
+ ASSERT.equal(0x44, buf[6]);
+ ASSERT.equal(0x23, buf[7]);
+ ASSERT.equal(0x00, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'little', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0x87, buf[2]);
+ ASSERT.equal(0x00, buf[3]);
+ ASSERT.equal(0x00, buf[4]);
+ ASSERT.equal(0x00, buf[5]);
+ ASSERT.equal(0x56, buf[6]);
+ ASSERT.equal(0x44, buf[7]);
+ ASSERT.equal(0x23, buf[8]);
+ ASSERT.equal(0x00, buf[9]);
+
+ data = [0xffff3421, 0x34abcdba];
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'big', buf, 0);
+ ASSERT.equal(0xff, buf[0]);
+ ASSERT.equal(0xff, buf[1]);
+ ASSERT.equal(0x34, buf[2]);
+ ASSERT.equal(0x21, buf[3]);
+ ASSERT.equal(0x34, buf[4]);
+ ASSERT.equal(0xab, buf[5]);
+ ASSERT.equal(0xcd, buf[6]);
+ ASSERT.equal(0xba, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0xff, buf[1]);
+ ASSERT.equal(0xff, buf[2]);
+ ASSERT.equal(0x34, buf[3]);
+ ASSERT.equal(0x21, buf[4]);
+ ASSERT.equal(0x34, buf[5]);
+ ASSERT.equal(0xab, buf[6]);
+ ASSERT.equal(0xcd, buf[7]);
+ ASSERT.equal(0xba, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'big', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0xff, buf[2]);
+ ASSERT.equal(0xff, buf[3]);
+ ASSERT.equal(0x34, buf[4]);
+ ASSERT.equal(0x21, buf[5]);
+ ASSERT.equal(0x34, buf[6]);
+ ASSERT.equal(0xab, buf[7]);
+ ASSERT.equal(0xcd, buf[8]);
+ ASSERT.equal(0xba, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'little', buf, 0);
+ ASSERT.equal(0xba, buf[0]);
+ ASSERT.equal(0xcd, buf[1]);
+ ASSERT.equal(0xab, buf[2]);
+ ASSERT.equal(0x34, buf[3]);
+ ASSERT.equal(0x21, buf[4]);
+ ASSERT.equal(0x34, buf[5]);
+ ASSERT.equal(0xff, buf[6]);
+ ASSERT.equal(0xff, buf[7]);
+ ASSERT.equal(0x66, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0xba, buf[1]);
+ ASSERT.equal(0xcd, buf[2]);
+ ASSERT.equal(0xab, buf[3]);
+ ASSERT.equal(0x34, buf[4]);
+ ASSERT.equal(0x21, buf[5]);
+ ASSERT.equal(0x34, buf[6]);
+ ASSERT.equal(0xff, buf[7]);
+ ASSERT.equal(0xff, buf[8]);
+ ASSERT.equal(0x66, buf[9]);
+
+ buf.fill(0x66);
+ mod_ctype.wuint64(data, 'little', buf, 2);
+ ASSERT.equal(0x66, buf[0]);
+ ASSERT.equal(0x66, buf[1]);
+ ASSERT.equal(0xba, buf[2]);
+ ASSERT.equal(0xcd, buf[3]);
+ ASSERT.equal(0xab, buf[4]);
+ ASSERT.equal(0x34, buf[5]);
+ ASSERT.equal(0x21, buf[6]);
+ ASSERT.equal(0x34, buf[7]);
+ ASSERT.equal(0xff, buf[8]);
+ ASSERT.equal(0xff, buf[9]);
+}
+
+/*
+ * Make sure we catch invalid writes.
+ */
+function testWriteInvalid()
+{
+ var data, buf;
+
+ /* Buffer too small */
+ buf = new Buffer(4);
+ data = [ 0, 0];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 0);
+ }, Error, 'buffer too small');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 0);
+ }, Error, 'buffer too small');
+
+ /* Beyond the end of the buffer */
+ buf = new Buffer(12);
+ data = [ 0, 0];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 11);
+ }, Error, 'write beyond end of buffer');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 11);
+ }, Error, 'write beyond end of buffer');
+
+ /* Write negative values */
+ buf = new Buffer(12);
+ data = [ -3, 0 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write negative number');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write negative number');
+
+ data = [ 0, -3 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write negative number');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write negative number');
+
+ data = [ -3, -3 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write negative number');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write negative number');
+
+
+ /* Write fractional values */
+ buf = new Buffer(12);
+ data = [ 3.33, 0 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ 0, 3.3 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ data = [ 3.33, 2.42 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write fractions');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write fractions');
+
+ /* Write values that are too large */
+ buf = new Buffer(12);
+ data = [ 0xffffffffff, 23 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ 0xffffffffff, 0xffffff238 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+
+ data = [ 0x23, 0xffffff238 ];
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'big', buf, 1);
+ }, Error, 'write too large');
+ ASSERT.throws(function () {
+ mod_ctype.wuint64(data, 'little', buf, 1);
+ }, Error, 'write too large');
+}
+
+
+testRead();
+testReadOver();
+testWriteZero();
+testWrite();
+testWriteInvalid();
--- /dev/null
+/*
+ * A battery of tests for sucessful round-trip between writes and reads
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+
+/*
+ * What the heck, let's just test every value for 8-bits.
+ */
+
+function test8() {
+ var data = new Buffer(1);
+ var i;
+ for (i = 0; i < 256; i++) {
+ mod_ctype.wuint8(i, 'big', data, 0);
+ ASSERT.equal(i, mod_ctype.ruint8(data, 'big', 0));
+ mod_ctype.wuint8(i, 'little', data, 0);
+ ASSERT.equal(i, mod_ctype.ruint8(data, 'little', 0));
+ }
+ ASSERT.ok(true);
+}
+
+/*
+ * Test a random sample of 256 values in the 16-bit unsigned range
+ */
+
+function test16() {
+ var data = new Buffer(2);
+ var i = 0;
+ for (i = 0; i < 256; i++) {
+ var value = Math.round(Math.random() * Math.pow(2, 16));
+ mod_ctype.wuint16(value, 'big', data, 0);
+ ASSERT.equal(value, mod_ctype.ruint16(data, 'big', 0));
+ mod_ctype.wuint16(value, 'little', data, 0);
+ ASSERT.equal(value, mod_ctype.ruint16(data, 'little', 0));
+ }
+}
+
+/*
+ * Test a random sample of 256 values in the 32-bit unsigned range
+ */
+
+function test32() {
+ var data = new Buffer(4);
+ var i = 0;
+ for (i = 0; i < 256; i++) {
+ var value = Math.round(Math.random() * Math.pow(2, 32));
+ mod_ctype.wuint32(value, 'big', data, 0);
+ ASSERT.equal(value, mod_ctype.ruint32(data, 'big', 0));
+ mod_ctype.wuint32(value, 'little', data, 0);
+ ASSERT.equal(value, mod_ctype.ruint32(data, 'little', 0));
+ }
+}
+
+/*
+ * Test a random sample of 256 values in the 64-bit unsigned range
+ */
+
+function test64() {
+ var data = new Buffer(8);
+ var i = 0;
+ for (i = 0; i < 256; i++) {
+ var low = Math.round(Math.random() * Math.pow(2, 32));
+ var high = Math.round(Math.random() * Math.pow(2, 32));
+ mod_ctype.wuint64([high, low], 'big', data, 0);
+ var result = mod_ctype.ruint64(data, 'big', 0);
+ ASSERT.equal(high, result[0]);
+ ASSERT.equal(low, result[1]);
+ mod_ctype.wuint64([high, low], 'little', data, 0);
+ result = mod_ctype.ruint64(data, 'little', 0);
+ ASSERT.equal(high, result[0]);
+ ASSERT.equal(low, result[1]);
+ }
+}
+
+exports.test8 = test8;
+exports.test16 = test16;
+exports.test32 = test32;
+exports.test64 = test64;
--- /dev/null
+/*
+ * A battery of tests to help us read a series of uints
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+/*
+ * We need to check the following things:
+ * - We are correctly resolving big endian (doesn't mean anything for 8 bit)
+ * - Correctly resolving little endian (doesn't mean anything for 8 bit)
+ * - Correctly using the offsets
+ * - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test8()
+{
+ var data = new Buffer(4);
+ data[0] = 23;
+ data[1] = 23;
+ data[2] = 23;
+ data[3] = 23;
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'big', 0));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'little', 0));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'big', 1));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'little', 1));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'big', 2));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'little', 2));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'big', 3));
+ ASSERT.equal(23, mod_ctype.ruint8(data, 'little', 3));
+ data[0] = 255; /* If it became a signed int, would be -1 */
+ ASSERT.equal(255, mod_ctype.ruint8(data, 'big', 0));
+ ASSERT.equal(255, mod_ctype.ruint8(data, 'little', 0));
+}
+
+/*
+ * Test 16 bit unsigned integers. We need to verify the same set as 8 bit, only
+ * now some of the issues actually matter:
+ * - We are correctly resolving big endian
+ * - Correctly resolving little endian
+ * - Correctly using the offsets
+ * - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test16()
+{
+ var data = new Buffer(4);
+ /* Test signed values first */
+ data[0] = 0;
+ data[1] = 0x23;
+ data[2] = 0x42;
+ data[3] = 0x3f;
+
+ ASSERT.equal(0x23, mod_ctype.ruint16(data, 'big', 0));
+ ASSERT.equal(0x2342, mod_ctype.ruint16(data, 'big', 1));
+ ASSERT.equal(0x423f, mod_ctype.ruint16(data, 'big', 2));
+
+ ASSERT.equal(0x2300, mod_ctype.ruint16(data, 'little', 0));
+ ASSERT.equal(0x4223, mod_ctype.ruint16(data, 'little', 1));
+ ASSERT.equal(0x3f42, mod_ctype.ruint16(data, 'little', 2));
+
+ data[0] = 0xfe;
+ data[1] = 0xfe;
+
+ ASSERT.equal(0xfefe, mod_ctype.ruint16(data, 'big', 0));
+ ASSERT.equal(0xfefe, mod_ctype.ruint16(data, 'little', 0));
+}
+
+/*
+ * Test 32 bit unsigned integers. We need to verify the same set as 8 bit, only
+ * now some of the issues actually matter:
+ * - We are correctly resolving big endian
+ * - Correctly using the offsets
+ * - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test32()
+{
+ var data = new Buffer(8);
+ data[0] = 0x32;
+ data[1] = 0x65;
+ data[2] = 0x42;
+ data[3] = 0x56;
+ data[4] = 0x23;
+ data[5] = 0xff;
+
+ ASSERT.equal(0x32654256, mod_ctype.ruint32(data, 'big', 0));
+ ASSERT.equal(0x65425623, mod_ctype.ruint32(data, 'big', 1));
+ ASSERT.equal(0x425623ff, mod_ctype.ruint32(data, 'big', 2));
+
+ ASSERT.equal(0x56426532, mod_ctype.ruint32(data, 'little', 0));
+ ASSERT.equal(0x23564265, mod_ctype.ruint32(data, 'little', 1));
+ ASSERT.equal(0xff235642, mod_ctype.ruint32(data, 'little', 2));
+}
+
+test8();
+test16();
+test32();
--- /dev/null
+/*
+ * A battery of tests to help us read a series of uints
+ */
+
+var mod_ctype = require('../../../ctio.js');
+var ASSERT = require('assert');
+
+/*
+ * We need to check the following things:
+ * - We are correctly resolving big endian (doesn't mean anything for 8 bit)
+ * - Correctly resolving little endian (doesn't mean anything for 8 bit)
+ * - Correctly using the offsets
+ * - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test8()
+{
+ var data = new Buffer(4);
+ mod_ctype.wuint8(23, 'big', data, 0);
+ mod_ctype.wuint8(23, 'big', data, 1);
+ mod_ctype.wuint8(23, 'big', data, 2);
+ mod_ctype.wuint8(23, 'big', data, 3);
+ ASSERT.equal(23, data[0]);
+ ASSERT.equal(23, data[1]);
+ ASSERT.equal(23, data[2]);
+ ASSERT.equal(23, data[3]);
+ mod_ctype.wuint8(23, 'little', data, 0);
+ mod_ctype.wuint8(23, 'little', data, 1);
+ mod_ctype.wuint8(23, 'little', data, 2);
+ mod_ctype.wuint8(23, 'little', data, 3);
+ ASSERT.equal(23, data[0]);
+ ASSERT.equal(23, data[1]);
+ ASSERT.equal(23, data[2]);
+ ASSERT.equal(23, data[3]);
+ mod_ctype.wuint8(255, 'big', data, 0);
+ ASSERT.equal(255, data[0]);
+ mod_ctype.wuint8(255, 'little', data, 0);
+ ASSERT.equal(255, data[0]);
+}
+
+function test16()
+{
+ var value = 0x2343;
+ var data = new Buffer(4);
+ mod_ctype.wuint16(value, 'big', data, 0);
+ ASSERT.equal(0x23, data[0]);
+ ASSERT.equal(0x43, data[1]);
+ mod_ctype.wuint16(value, 'big', data, 1);
+ ASSERT.equal(0x23, data[1]);
+ ASSERT.equal(0x43, data[2]);
+ mod_ctype.wuint16(value, 'big', data, 2);
+ ASSERT.equal(0x23, data[2]);
+ ASSERT.equal(0x43, data[3]);
+
+ mod_ctype.wuint16(value, 'little', data, 0);
+ ASSERT.equal(0x23, data[1]);
+ ASSERT.equal(0x43, data[0]);
+
+ mod_ctype.wuint16(value, 'little', data, 1);
+ ASSERT.equal(0x23, data[2]);
+ ASSERT.equal(0x43, data[1]);
+
+ mod_ctype.wuint16(value, 'little', data, 2);
+ ASSERT.equal(0x23, data[3]);
+ ASSERT.equal(0x43, data[2]);
+
+ value = 0xff80;
+ mod_ctype.wuint16(value, 'little', data, 0);
+ ASSERT.equal(0xff, data[1]);
+ ASSERT.equal(0x80, data[0]);
+
+ mod_ctype.wuint16(value, 'big', data, 0);
+ ASSERT.equal(0xff, data[0]);
+ ASSERT.equal(0x80, data[1]);
+}
+
+function test32()
+{
+ var data = new Buffer(6);
+ var value = 0xe7f90a6d;
+
+ mod_ctype.wuint32(value, 'big', data, 0);
+ ASSERT.equal(0xe7, data[0]);
+ ASSERT.equal(0xf9, data[1]);
+ ASSERT.equal(0x0a, data[2]);
+ ASSERT.equal(0x6d, data[3]);
+
+ mod_ctype.wuint32(value, 'big', data, 1);
+ ASSERT.equal(0xe7, data[1]);
+ ASSERT.equal(0xf9, data[2]);
+ ASSERT.equal(0x0a, data[3]);
+ ASSERT.equal(0x6d, data[4]);
+
+ mod_ctype.wuint32(value, 'big', data, 2);
+ ASSERT.equal(0xe7, data[2]);
+ ASSERT.equal(0xf9, data[3]);
+ ASSERT.equal(0x0a, data[4]);
+ ASSERT.equal(0x6d, data[5]);
+
+ mod_ctype.wuint32(value, 'little', data, 0);
+ ASSERT.equal(0xe7, data[3]);
+ ASSERT.equal(0xf9, data[2]);
+ ASSERT.equal(0x0a, data[1]);
+ ASSERT.equal(0x6d, data[0]);
+
+ mod_ctype.wuint32(value, 'little', data, 1);
+ ASSERT.equal(0xe7, data[4]);
+ ASSERT.equal(0xf9, data[3]);
+ ASSERT.equal(0x0a, data[2]);
+ ASSERT.equal(0x6d, data[1]);
+
+ mod_ctype.wuint32(value, 'little', data, 2);
+ ASSERT.equal(0xe7, data[5]);
+ ASSERT.equal(0xf9, data[4]);
+ ASSERT.equal(0x0a, data[3]);
+ ASSERT.equal(0x6d, data[2]);
+}
+
+function test64()
+{
+ var data = new Buffer(10);
+ var value = 0x0007cda8e7f90a6d;
+ var high = Math.floor(value / Math.pow(2, 32));
+ var low = value - (high * Math.pow(2, 32));
+ ASSERT.equal(0x0007cda8, high);
+ ASSERT.equal(0xe7f90a6d, low);
+
+ mod_ctype.wuint64([high, low], 'big', data, 0);
+ ASSERT.equal(0x00, data[0]);
+ ASSERT.equal(0x07, data[1]);
+ ASSERT.equal(0xcd, data[2]);
+ ASSERT.equal(0xa8, data[3]);
+ ASSERT.equal(0xe7, data[4]);
+ ASSERT.equal(0xf9, data[5]);
+ ASSERT.equal(0x0a, data[6]);
+ ASSERT.equal(0x6d, data[7]);
+
+ mod_ctype.wuint64([high, low], 'little', data, 0);
+ ASSERT.equal(0x6d, data[0]);
+ ASSERT.equal(0x0a, data[1]);
+ ASSERT.equal(0xf9, data[2]);
+ ASSERT.equal(0xe7, data[3]);
+ ASSERT.equal(0xa8, data[4]);
+ ASSERT.equal(0xcd, data[5]);
+ ASSERT.equal(0x07, data[6]);
+ ASSERT.equal(0x00, data[7]);
+}
+
+test8();
+test16();
+test32();
+test64();
+
+exports.test8 = test8;
+exports.test16 = test16;
+exports.test32 = test32;
+exports.test64 = test64;
--- /dev/null
+/*
+ * Simple does to see if it works at all
+ */
+var mod_ctype = require('../../ctype');
+var ASSERT = require('assert');
+var mod_sys = require('sys');
+
+function test()
+{
+ var ii, p, result, buffer;
+
+ p = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer(4);
+ buffer[0] = 23;
+ buffer[3] = 42;
+ result = p.readData([ { x: { type: 'uint8_t' }},
+ { y: { type: 'uint8_t', offset: 3 }}
+ ], buffer, 0);
+ ASSERT.equal(23, result['x']);
+ ASSERT.equal(42, result['y']);
+
+ buffer = new Buffer(23);
+ for (ii = 0; ii < 23; ii++)
+ buffer[ii] = 0;
+
+ buffer.write('Hello, world!');
+ result = p.readData([ { x: { type: 'char[20]' }} ], buffer, 0);
+
+ /*
+ * This is currently broken behvaior, need to redesign check
+ * ASSERT.equal('Hello, world!', result['x'].toString('utf-8', 0,
+ * result['x'].length));
+ */
+
+ buffer = new Buffer(4);
+ buffer[0] = 0x03;
+ buffer[1] = 0x24;
+ buffer[2] = 0x25;
+ buffer[3] = 0x26;
+ result = p.readData([ { y: { type: 'uint8_t' }},
+ { x: { type: 'uint8_t[y]' }}], buffer, 0);
+ console.log(mod_sys.inspect(result, true));
+
+ p.typedef('ssize_t', 'int32_t');
+ ASSERT.deepEqual({ 'ssize_t': 'int32_t' }, p.lstypes());
+ result = p.readData([ { x: { type: 'ssize_t' } } ], buffer, 0);
+ ASSERT.equal(0x26252403, result['x']);
+}
+
+test();
--- /dev/null
+/*
+ * Simple does it fucking work at all test
+ */
+
+var mod_ctype = require('../../ctype');
+var ASSERT = require('assert');
+var mod_sys = require('sys');
+
+function test()
+{
+ var ii, p, buffer, buf2;
+
+ p = new mod_ctype.Parser({ endian: 'big' });
+ buffer = new Buffer(4);
+ p.writeData([ { x: { type: 'uint8_t', value: 23 }},
+ { y: { type: 'uint8_t', offset: 3, value: 42 }}
+ ], buffer, 0);
+ ASSERT.equal(23, buffer[0]);
+ ASSERT.equal(42, buffer[3]);
+
+ buffer = new Buffer(20);
+ for (ii = 0; ii < 20; ii++)
+ buffer[ii] = 0;
+
+ buffer.write('Hello, world!');
+ buf2 = new Buffer(22);
+ p.writeData([ { x: { type: 'char[20]', value: buffer }} ], buf2, 0);
+ for (ii = 0; ii < 20; ii++)
+ ASSERT.equal(buffer[ii], buf2[ii]);
+ /*
+ * This is currently broken behvaior, need to redesign check
+ * ASSERT.equal('Hello, world!', result['x'].toString('utf-8', 0,
+ * result['x'].length));
+ */
+
+ buffer = new Buffer(4);
+ p.writeData([ { y: { type: 'uint8_t', value: 3 }},
+ { x: { type: 'uint8_t[y]', value: [ 0x24, 0x25, 0x26] }}],
+ buffer, 0);
+ console.log(mod_sys.inspect(buffer));
+
+ p.typedef('ssize_t', 'int32_t');
+ ASSERT.deepEqual({ 'ssize_t': 'int32_t' }, p.lstypes());
+}
--- /dev/null
+/*
+ * Test the different forms of reading characters:
+ *
+ * - the default, a single element buffer
+ * - uint8, values are uint8_ts
+ * - int8, values are int8_ts
+ */
+var mod_ctype = require('../../ctype');
+var mod_assert = require('assert');
+
+function test()
+{
+ var p, buf, res;
+
+ buf = new Buffer(1);
+ buf[0] = 255;
+
+ p = new mod_ctype.Parser({ endian: 'little'});
+ res = p.readData([ { c: { type: 'char' }} ], buf, 0);
+ res = res['c'];
+ mod_assert.ok(res instanceof Buffer);
+ mod_assert.equal(255, res[0]);
+
+ p = new mod_ctype.Parser({ endian: 'little',
+ 'char-type': 'int8' });
+ res = p.readData([ { c: { type: 'char' }} ], buf, 0);
+ res = res['c'];
+ mod_assert.ok(typeof (res) == 'number', 'got typeof (res): ' +
+ typeof (res));
+ mod_assert.equal(-1, res);
+
+ p = new mod_ctype.Parser({ endian: 'little',
+ 'char-type': 'uint8' });
+ res = p.readData([ { c: { type: 'char' }} ], buf, 0);
+ res = res['c'];
+ mod_assert.ok(typeof (res) == 'number', 'got typeof (res): ' +
+ typeof (res));
+ mod_assert.equal(255, res);
+
+}
+
+test();
--- /dev/null
+/*
+ * Simple test to make sure that the endian setting works.
+ */
+
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var parser, buf;
+
+ parser = new mod_ctype.Parser({
+ endian: 'little'
+ });
+
+ buf = new Buffer(2);
+ parser.writeData([ { key: { type: 'uint16_t' } } ], buf, 0, [ 0x1234 ]);
+ mod_assert.equal(buf[0], 0x34);
+ mod_assert.equal(buf[1], 0x12);
+ parser.setEndian('big');
+
+ parser.writeData([ { key: { type: 'uint16_t' } } ], buf, 0, [ 0x1234 ]);
+ mod_assert.equal(buf[0], 0x12);
+ mod_assert.equal(buf[1], 0x34);
+
+ parser.setEndian('little');
+ parser.writeData([ { key: { type: 'uint16_t' } } ], buf, 0, [ 0x1234 ]);
+ mod_assert.equal(buf[0], 0x34);
+ mod_assert.equal(buf[1], 0x12);
+}
+
+function fail()
+{
+ var parser;
+
+ parser = new mod_ctype.Parser({
+ endian: 'little'
+ });
+ mod_assert.throws(function () {
+ parser.setEndian('littlebigwrong');
+ });
+}
+
+test();
+fail();
--- /dev/null
+/*
+ * A long overdue test to go through and verify that we can read and write
+ * structures as well as nested structures.
+ */
+
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var parser, buf, data;
+ parser = new mod_ctype.Parser({
+ endian: 'little'
+ });
+ parser.typedef('point_t', [
+ { x: { type: 'uint8_t' } },
+ { y: { type: 'uint8_t' } }
+ ]);
+ buf = new Buffer(2);
+ data = [
+ { point: { type: 'point_t', value: [ 23, 42 ] } }
+ ];
+ parser.writeData(data, buf, 0);
+ mod_assert.ok(buf[0] == 23);
+ mod_assert.ok(buf[1] == 42);
+}
+
+test();
--- /dev/null
+/*
+ * Testing to ensure we're reading the expected number bytes
+ */
+var mod_ctype = require('../../ctype');
+var ASSERT = require('assert');
+
+function testUint8()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('80', 'hex');
+ result = parser.readStruct([ { item: { type: 'uint8_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 1);
+}
+
+function testSint8()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('80', 'hex');
+ result = parser.readStruct([ { item: { type: 'int8_t' } } ], buffer, 0);
+ ASSERT.equal(result['size'], 1);
+}
+
+function testUint16()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('8000', 'hex');
+ result = parser.readStruct([ { item: { type: 'uint16_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 2);
+}
+
+function testSint16()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('8000', 'hex');
+ result = parser.readStruct([ { item: { type: 'int16_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 2);
+}
+
+function testUint32()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('80000000', 'hex');
+ result = parser.readStruct([ { item: { type: 'uint32_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 4);
+}
+
+function testSint32()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('80000000', 'hex');
+ result = parser.readStruct([ { item: { type: 'int32_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 4);
+}
+
+function testUint64()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('8000000000000000', 'hex');
+ result = parser.readStruct([ { item: { type: 'uint64_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 8);
+}
+
+function testSint64()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('8000000000000000', 'hex');
+ result = parser.readStruct([ { item: { type: 'int64_t' } } ], buffer,
+ 0);
+ ASSERT.equal(result['size'], 8);
+}
+
+function testFloat()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('ABAAAA3E', 'hex');
+ result = parser.readStruct([ { item: { type: 'float' } } ], buffer, 0);
+ ASSERT.equal(result['size'], 4);
+}
+
+function testDouble()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('000000000000F03F', 'hex');
+ result = parser.readStruct([ { item: { type: 'double' } } ], buffer, 0);
+ ASSERT.equal(result['size'], 8);
+}
+
+function testChar()
+{
+ var parser, result, buffer;
+ parser = new mod_ctype.Parser({ endian: 'little' });
+ buffer = new Buffer('t');
+ result = parser.readStruct([ { item: { type: 'char' } } ], buffer, 0);
+ ASSERT.equal(result['size'], 1);
+}
+
+function test()
+{
+ testSint8();
+ testUint8();
+ testSint16();
+ testUint16();
+ testSint32();
+ testUint32();
+ testSint64();
+ testUint64();
+ testFloat();
+ testDouble();
+ testChar();
+}
+
+test();
--- /dev/null
+/*
+ * A long overdue test to go through and verify that we can read and write
+ * structures as well as nested structures.
+ */
+
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var parser, buf, data;
+ parser = new mod_ctype.Parser({
+ endian: 'little'
+ });
+ parser.typedef('point_t', [
+ { x: { type: 'uint8_t' } },
+ { y: { type: 'uint8_t' } }
+ ]);
+ buf = new Buffer(2);
+ data = [
+ { point: { type: 'point_t' } }
+ ];
+ parser.writeData(data, buf, 0, [ [ 23, 42 ] ]);
+ mod_assert.ok(buf[0] == 23);
+ mod_assert.ok(buf[1] == 42);
+}
+
+test();
--- /dev/null
+/*
+ * Test to verify that the offset is incremented when structures are written to.
+ * Hopefully we will not regress issue #41
+ */
+
+var mod_ctype = require('../../ctype.js');
+var mod_assert = require('assert');
+
+function test()
+{
+ var parser, buf, data;
+ parser = new mod_ctype.Parser({
+ endian: 'little'
+ });
+ parser.typedef('point_t', [
+ { x: { type: 'uint8_t' } },
+ { y: { type: 'uint8_t' } }
+ ]);
+ buf = new Buffer(4);
+ data = [
+ { point1: { type: 'point_t' } },
+ { point2: { type: 'point_t' } }
+ ];
+ parser.writeData(data, buf, 0, [ [ 23, 42 ], [ 91, 18 ] ]);
+ mod_assert.ok(buf[0] == 23);
+ mod_assert.ok(buf[1] == 42);
+ mod_assert.ok(buf[2] == 91);
+ mod_assert.ok(buf[3] == 18);
+}
+
+test();
--- /dev/null
+{
+ "author": {
+ "name": "Joyent, Inc"
+ },
+ "name": "http-signature",
+ "description": "Reference implementation of Joyent's HTTP Signature Scheme",
+ "version": "0.9.11",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/joyent/node-http-signature.git"
+ },
+ "engines": {
+ "node": ">=0.8"
+ },
+ "main": "lib/index.js",
+ "scripts": {
+ "test": "./node_modules/.bin/tap tst/*.js"
+ },
+ "dependencies": {
+ "assert-plus": "0.1.2",
+ "asn1": "0.1.11",
+ "ctype": "0.5.2"
+ },
+ "devDependencies": {
+ "node-uuid": "1.4.0",
+ "tap": "0.3.1"
+ },
+ "readme": "node-http-signature is a node.js library that has client and server components\nfor Joyent's `HTTP Signature Scheme`.\n\n## Usage\n\nNote the example below signs a request with the same key/cert used to start an\nHTTP server. This is almost certainly not what you actaully want, but is just\nused to illustrate the API calls; you will need to provide your own key\nmanagement in addition to this library.\n\n### Client\n\n var fs = require('fs');\n var https = require('https');\n var httpSignature = require('http-signature');\n\n var key = fs.readFileSync('./key.pem', 'ascii');\n\n var options = {\n host: 'localhost',\n port: 8443,\n path: '/',\n method: 'GET',\n headers: {}\n };\n\n // Adds a 'Date' header in, signs it, and adds the\n // 'Authorization' header in.\n var req = https.request(options, function(res) {\n console.log(res.statusCode);\n });\n\n\n httpSignature.sign(req, {\n key: key,\n keyId: './cert.pem'\n });\n\n req.end();\n\n### Server\n\n var fs = require('fs');\n var https = require('https');\n var httpSignature = require('http-signature');\n\n var options = {\n key: fs.readFileSync('./key.pem'),\n cert: fs.readFileSync('./cert.pem')\n };\n\n https.createServer(options, function (req, res) {\n var rc = 200;\n var parsed = httpSignature.parseRequest(req);\n var pub = fs.readFileSync(parsed.keyId, 'ascii');\n if (!httpSignature.verifySignature(parsed, pub))\n rc = 401;\n\n res.writeHead(rc);\n res.end();\n }).listen(8443);\n\n## Installation\n\n npm install http-signature\n\n## License\n\nMIT.\n\n## Bugs\n\nSee <https://github.com/joyent/node-http-signature/issues>.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/joyent/node-http-signature/issues"
+ },
+ "_id": "http-signature@0.9.11",
+ "_from": "http-signature@~0.9.11"
+}
{
"name": "json-stringify-safe",
- "version": "3.0.0",
+ "version": "4.0.0",
"description": "Like JSON.stringify, but doesn't blow up on circular refs",
"main": "stringify.js",
"scripts": {
"license": "BSD",
"readmeFilename": "README.md",
"readme": "# json-stringify-safe\n\nLike JSON.stringify, but doesn't throw on circular references.\n\n## Usage\n\nTakes the same arguments as `JSON.stringify`.\n\n```javascript\nvar stringify = require('json-stringify-safe');\nvar circularObj = {};\ncircularObj.circularRef = circularObj;\ncircularObj.list = [ circularObj, circularObj ];\nconsole.log(stringify(circularObj, null, 2));\n```\n\nOutput:\n\n```json\n{\n \"circularRef\": \"[Circular]\",\n \"list\": [\n \"[Circular]\",\n \"[Circular]\"\n ]\n}\n```\n",
- "_id": "json-stringify-safe@3.0.0",
- "dist": {
- "shasum": "9db7b0e530c7f289c5e8c8432af191c2ff75a5b3"
+ "bugs": {
+ "url": "https://github.com/isaacs/json-stringify-safe/issues"
},
- "_from": "json-stringify-safe@~3.0.0",
- "_resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-3.0.0.tgz"
+ "_id": "json-stringify-safe@4.0.0",
+ "_from": "json-stringify-safe@~4.0.0"
}
module.exports = stringify;
-function getSerialize (fn) {
+function getSerialize (fn, decycle) {
var seen = [];
+ decycle = decycle || function(key, value) {
+ return '[Circular]';
+ };
return function(key, value) {
var ret = value;
if (typeof value === 'object' && value) {
if (seen.indexOf(value) !== -1)
- ret = '[Circular]';
+ ret = decycle(key, value);
else
seen.push(value);
}
}
}
-function stringify(obj, fn, spaces) {
- return JSON.stringify(obj, getSerialize(fn), spaces);
+function stringify(obj, fn, spaces, decycle) {
+ return JSON.stringify(obj, getSerialize(fn, decycle), spaces);
}
stringify.getSerialize = getSerialize;
var stringify = require('./stringify.js');
-var circularObj = {};
+var circularObj = { a: 'b' };
circularObj.circularRef = circularObj;
circularObj.list = [ circularObj, circularObj ];
+//////////
+// default
var testObj = {
+ "a": "b",
"circularRef": "[Circular]",
"list": [
"[Circular]",
assert.equal(JSON.stringify(testObj, null, 2),
JSON.stringify(circularObj, stringify.getSerialize(), 2));
+
+////////
+// prune
+testObj = {
+ "a": "b",
+ "list": [
+ null,
+ null
+ ]
+};
+
+function prune(k, v) {}
+
+assert.equal(JSON.stringify(testObj, null, 2),
+ stringify(circularObj, null, 2, prune));
+
+///////////
+// re-cycle
+// (throws)
+function recycle(k, v) {
+ return v;
+}
+
+assert.throws(function() {
+ stringify(circularObj, null, 2, recycle);
+});
+
+////////
+// fancy
+testObj = {
+ "a": "b",
+ "circularRef": "circularRef{a:string,circularRef:Object,list:Array}",
+ "list": [
+ "0{a:string,circularRef:Object,list:Array}",
+ "1{a:string,circularRef:Object,list:Array}"
+ ]
+};
+
+function signer(key, value) {
+ var ret = key + '{';
+ var f = false;
+ for (var i in value) {
+ if (f)
+ ret += ',';
+ f = true;
+ ret += i + ':';
+ var v = value[i];
+ switch (typeof v) {
+ case 'object':
+ if (!v)
+ ret += 'null';
+ else if (Array.isArray(v))
+ ret += 'Array'
+ else
+ ret += v.constructor && v.constructor.name || 'Object';
+ break;
+ default:
+ ret += typeof v;
+ break;
+ }
+ }
+ ret += '}';
+ return ret;
+}
+
+assert.equal(JSON.stringify(testObj, null, 2),
+ stringify(circularObj, null, 2, signer));
+
+
+////////
+// pass!
console.log('ok');
"version": "1.2.9",
"readme": "# mime\n\nComprehensive MIME type mapping API. Includes all 600+ types and 800+ extensions defined by the Apache project, plus additional types submitted by the node.js community.\n\n## Install\n\nInstall with [npm](http://github.com/isaacs/npm):\n\n npm install mime\n\n## API - Queries\n\n### mime.lookup(path)\nGet the mime type associated with a file. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g.\n\n var mime = require('mime');\n\n mime.lookup('/path/to/file.txt'); // => 'text/plain'\n mime.lookup('file.txt'); // => 'text/plain'\n mime.lookup('.TXT'); // => 'text/plain'\n mime.lookup('htm'); // => 'text/html'\n\n### mime.extension(type)\nGet the default extension for `type`\n\n mime.extension('text/html'); // => 'html'\n mime.extension('application/octet-stream'); // => 'bin'\n\n### mime.charsets.lookup()\n\nMap mime-type to charset\n\n mime.charsets.lookup('text/plain'); // => 'UTF-8'\n\n(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.)\n\n## API - Defining Custom Types\n\nThe following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/broofa/node-mime/wiki/Requesting-New-Types).\n\n### mime.define()\n\nAdd custom mime/extension mappings\n\n mime.define({\n 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'],\n 'application/x-my-type': ['x-mt', 'x-mtt'],\n // etc ...\n });\n\n mime.lookup('x-sft'); // => 'text/x-some-format'\n\nThe first entry in the extensions array is returned by `mime.extension()`. E.g.\n\n mime.extension('text/x-some-format'); // => 'x-sf'\n\n### mime.load(filepath)\n\nLoad mappings from an Apache \".types\" format file\n\n mime.load('./my_project.types');\n\nThe .types file format is simple - See the `types` dir for examples.\n",
"readmeFilename": "README.md",
- "_id": "mime@1.2.9",
- "dist": {
- "shasum": "7991c2427950d1dc2fa64cfd7d6b6326013a6156"
+ "bugs": {
+ "url": "https://github.com/broofa/node-mime/issues"
},
- "_from": "mime@~1.2.7",
- "_resolved": "https://registry.npmjs.org/mime/-/mime-1.2.9.tgz"
+ "_id": "mime@1.2.9",
+ "_from": "mime@~1.2.9"
}
"version": "1.4.0",
"readme": "# node-uuid\n\nSimple, fast generation of [RFC4122](http://www.ietf.org/rfc/rfc4122.txt) UUIDS.\n\nFeatures:\n\n* Generate RFC4122 version 1 or version 4 UUIDs\n* Runs in node.js and all browsers.\n* Cryptographically strong random # generation on supporting platforms\n* 1.1K minified and gzip'ed (Want something smaller? Check this [crazy shit](https://gist.github.com/982883) out! )\n* [Annotated source code](http://broofa.github.com/node-uuid/docs/uuid.html)\n\n## Getting Started\n\nInstall it in your browser:\n\n```html\n<script src=\"uuid.js\"></script>\n```\n\nOr in node.js:\n\n```\nnpm install node-uuid\n```\n\n```javascript\nvar uuid = require('node-uuid');\n```\n\nThen create some ids ...\n\n```javascript\n// Generate a v1 (time-based) id\nuuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'\n\n// Generate a v4 (random) id\nuuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'\n```\n\n## API\n\n### uuid.v1([`options` [, `buffer` [, `offset`]]])\n\nGenerate and return a RFC4122 v1 (timestamp-based) UUID.\n\n* `options` - (Object) Optional uuid state to apply. Properties may include:\n\n * `node` - (Array) Node id as Array of 6 bytes (per 4.1.6). Default: Randomly generated ID. See note 1.\n * `clockseq` - (Number between 0 - 0x3fff) RFC clock sequence. Default: An internally maintained clockseq is used.\n * `msecs` - (Number | Date) Time in milliseconds since unix Epoch. Default: The current time is used.\n * `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.\n\n* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written.\n* `offset` - (Number) Starting index in `buffer` at which to begin writing.\n\nReturns `buffer`, if specified, otherwise the string form of the UUID\n\nNotes:\n\n1. 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.)\n\nExample: Generate string UUID with fully-specified options\n\n```javascript\nuuid.v1({\n node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],\n clockseq: 0x1234,\n msecs: new Date('2011-11-01').getTime(),\n nsecs: 5678\n}); // -> \"710b962e-041c-11e1-9234-0123456789ab\"\n```\n\nExample: In-place generation of two binary IDs\n\n```javascript\n// Generate two ids in an array\nvar arr = new Array(32); // -> []\nuuid.v1(null, arr, 0); // -> [02 a2 ce 90 14 32 11 e1 85 58 0b 48 8e 4f c1 15]\nuuid.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]\n\n// Optionally use uuid.unparse() to get stringify the ids\nuuid.unparse(buffer); // -> '02a2ce90-1432-11e1-8558-0b488e4fc115'\nuuid.unparse(buffer, 16) // -> '02a31cb0-1432-11e1-8558-0b488e4fc115'\n```\n\n### uuid.v4([`options` [, `buffer` [, `offset`]]])\n\nGenerate and return a RFC4122 v4 UUID.\n\n* `options` - (Object) Optional uuid state to apply. Properties may include:\n\n * `random` - (Number[16]) Array of 16 numbers (0-255) to use in place of randomly generated values\n * `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.\n\n* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written.\n* `offset` - (Number) Starting index in `buffer` at which to begin writing.\n\nReturns `buffer`, if specified, otherwise the string form of the UUID\n\nExample: Generate string UUID with fully-specified options\n\n```javascript\nuuid.v4({\n random: [\n 0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0xc1, 0xea,\n 0x71, 0xb4, 0xef, 0xe1, 0x67, 0x1c, 0x58, 0x36\n ]\n});\n// -> \"109156be-c4fb-41ea-b1b4-efe1671c5836\"\n```\n\nExample: Generate two IDs in a single buffer\n\n```javascript\nvar buffer = new Array(32); // (or 'new Buffer' in node.js)\nuuid.v4(null, buffer, 0);\nuuid.v4(null, buffer, 16);\n```\n\n### uuid.parse(id[, buffer[, offset]])\n### uuid.unparse(buffer[, offset])\n\nParse and unparse UUIDs\n\n * `id` - (String) UUID(-like) string\n * `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written. Default: A new Array or Buffer is used\n * `offset` - (Number) Starting index in `buffer` at which to begin writing. Default: 0\n\nExample parsing and unparsing a UUID string\n\n```javascript\nvar 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>\nvar string = uuid.unparse(bytes); // -> '797ff043-11eb-11e1-80d6-510998755d10'\n```\n\n### uuid.noConflict()\n\n(Browsers only) Set `uuid` property back to it's previous value.\n\nReturns the node-uuid object.\n\nExample:\n\n```javascript\nvar myUuid = uuid.noConflict();\nmyUuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'\n```\n\n## Deprecated APIs\n\nSupport for the following v1.2 APIs is available in v1.3, but is deprecated and will be removed in the next major version.\n\n### uuid([format [, buffer [, offset]]])\n\nuuid() 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).\n\n### uuid.BufferClass\n\nThe 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.\n\n## Testing\n\nIn node.js\n\n```\n> cd test\n> node test.js\n```\n\nIn Browser\n\n```\nopen test/test.html\n```\n\n### Benchmarking\n\nRequires node.js\n\n```\nnpm install uuid uuid-js\nnode benchmark/benchmark.js\n```\n\nFor 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)\n\nFor browser performance [checkout the JSPerf tests](http://jsperf.com/node-uuid-performance).\n\n## Release notes\n\nv1.4\n* Improved module context detection\n* Removed public RNG functions\n\nv1.3.2:\n* Improve tests and handling of v1() options (Issue #24)\n* Expose RNG option to allow for perf testing with different generators\n\nv1.3:\n* Support for version 1 ids, thanks to [@ctavan](https://github.com/ctavan)!\n* Support for node.js crypto API\n* De-emphasizing performance in favor of a) cryptographic quality PRNGs where available and b) more manageable code\n",
"readmeFilename": "README.md",
- "_id": "node-uuid@1.4.0",
- "dist": {
- "shasum": "3e31c601803b006ef9a075ad64e7ea878211fbbf"
+ "bugs": {
+ "url": "https://github.com/broofa/node-uuid/issues"
},
- "_from": "node-uuid@~1.4.0",
- "_resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.0.tgz"
+ "_id": "node-uuid@1.4.0",
+ "_from": "node-uuid@~1.4.0"
}
--- /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.
},
"name": "oauth-sign",
"description": "OAuth 1 signing. Formerly a vendor lib in mikeal/request, now a standalone module.",
- "version": "0.2.0",
+ "version": "0.3.0",
"repository": {
"url": "https://github.com/mikeal/oauth-sign"
},
"scripts": {
"test": "node test.js"
},
- "_id": "oauth-sign@0.2.0",
- "readme": "ERROR: No README.md file found!",
- "dist": {
- "shasum": "67e1fe3d8a3ecd51ec085246bb3395997b618db7"
+ "readme": "oauth-sign\n==========\n\nOAuth 1 signing. Formerly a vendor lib in mikeal/request, now a standalone module. \n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/mikeal/oauth-sign/issues"
},
- "_from": "oauth-sign@~0.2.0",
- "_resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.2.0.tgz"
+ "_id": "oauth-sign@0.3.0",
+ "_from": "oauth-sign@~0.3.0"
}
-node_modules
+test
+.travis.yml
+benchmark.js
+component.json
+examples.js
+History.md
+Makefile
+++ /dev/null
-
-0.5.5 / 2013-03-20
-==================
-
- * output key= for null or undefined values. Closes #52
-
-0.5.4 / 2013-03-15
-==================
-
- * fix ignoring of null / undefined. Closes #44
-
-0.5.3 2012-12-09
-==================
-
- * add info to component.json
- * remove regular client-side ./querystring.js, fix component.json support
-
-0.5.2 / 2012-11-14
-==================
-
- * fix uri encoding of non-plain object string values
-
-0.5.1 / 2012-09-18
-==================
-
- * fix encoded `=`. Closes #43
-
-0.5.0 / 2012-05-04
-==================
-
- * Added component support
-
-0.4.2 / 2012-02-08
-==================
-
- * Fixed: ensure objects are created when appropriate not arrays [aheckmann]
-
-0.4.1 / 2012-01-26
-==================
-
- * Fixed stringify()ing numbers. Closes #23
-
-0.4.0 / 2011-11-21
-==================
-
- * Allow parsing of an existing object (for `bodyParser()`) [jackyz]
- * Replaced expresso with mocha
-
-0.3.2 / 2011-11-08
-==================
-
- * Fixed global variable leak
-
-0.3.1 / 2011-08-17
-==================
-
- * Added `try/catch` around malformed uri components
- * Add test coverage for Array native method bleed-though
-
-0.3.0 / 2011-07-19
-==================
-
- * Allow `array[index]` and `object[property]` syntaxes [Aria Stewart]
-
-0.2.0 / 2011-06-29
-==================
-
- * Added `qs.stringify()` [Cory Forsyth]
-
-0.1.0 / 2011-04-13
-==================
-
- * Added jQuery-ish array support
-
-0.0.7 / 2011-03-13
-==================
-
- * Fixed; handle empty string and `== null` in `qs.parse()` [dmit]
- allows for convenient `qs.parse(url.parse(str).query)`
-
-0.0.6 / 2011-02-14
-==================
-
- * Fixed; support for implicit arrays
-
-0.0.4 / 2011-02-09
-==================
-
- * Fixed `+` as a space
-
-0.0.3 / 2011-02-08
-==================
-
- * Fixed case when right-hand value contains "]"
-
-0.0.2 / 2011-02-07
-==================
-
- * Fixed "=" presence in key
-
-0.0.1 / 2011-02-07
-==================
-
- * Initial release
+++ /dev/null
-
-test:
- @./node_modules/.bin/mocha \
- --ui bdd
-
-.PHONY: test
\ No newline at end of file
+++ /dev/null
-
-var qs = require('./');
-
-var times = 100000
- , start = new Date
- , n = times;
-
-console.log('times: %d', times);
-
-while (n--) qs.parse('foo=bar');
-console.log('simple: %dms', new Date - start);
-
-var start = new Date
- , n = times;
-
-while (n--) qs.parse('user[name][first]=tj&user[name][last]=holowaychuk');
-console.log('nested: %dms', new Date - start);
\ No newline at end of file
+++ /dev/null
-{
- "name": "querystring",
- "repo": "visionmedia/node-querystring",
- "description": "query-string parser / stringifier with nesting support",
- "version": "0.5.5",
- "keywords": ["querystring", "query", "parser"],
- "scripts": ["index.js"],
- "license": "MIT"
-}
+++ /dev/null
-
-/**
- * Module dependencies.
- */
-
-var qs = require('./');
-
-var obj = qs.parse('foo');
-console.log(obj)
-
-var obj = qs.parse('foo=bar=baz');
-console.log(obj)
-
-var obj = qs.parse('users[]');
-console.log(obj)
-
-var obj = qs.parse('name=tj&email=tj@vision-media.ca');
-console.log(obj)
-
-var obj = qs.parse('users[]=tj&users[]=tobi&users[]=jane');
-console.log(obj)
-
-var obj = qs.parse('user[name][first]=tj&user[name][last]=holowaychuk');
-console.log(obj)
-
-var obj = qs.parse('users[][name][first]=tj&users[][name][last]=holowaychuk');
-console.log(obj)
-
-var obj = qs.parse('a=a&a=b&a=c');
-console.log(obj)
-
-var obj = qs.parse('user[tj]=tj&user[tj]=TJ');
-console.log(obj)
-
-var obj = qs.parse('user[names]=tj&user[names]=TJ&user[names]=Tyler');
-console.log(obj)
-
-var obj = qs.parse('user[name][first]=tj&user[name][first]=TJ');
-console.log(obj)
-
-var obj = qs.parse('user[0]=tj&user[1]=TJ');
-console.log(obj)
-
-var obj = qs.parse('user[0]=tj&user[]=TJ');
-console.log(obj)
-
-var obj = qs.parse('user[0]=tj&user[foo]=TJ');
-console.log(obj)
-
-var str = qs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }});
-console.log(str);
\ No newline at end of file
-
/**
* Object#toString() ref for stringify().
*/
var toString = Object.prototype.toString;
/**
+ * Object#hasOwnProperty ref
+ */
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+/**
+ * Array#indexOf shim.
+ */
+
+var indexOf = typeof Array.prototype.indexOf === 'function'
+ ? function(arr, el) { return arr.indexOf(el); }
+ : function(arr, el) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === el) return i;
+ }
+ return -1;
+ };
+
+/**
+ * Array.isArray shim.
+ */
+
+var isArray = Array.isArray || function(arr) {
+ return toString.call(arr) == '[object Array]';
+};
+
+/**
+ * Object.keys shim.
+ */
+
+var objectKeys = Object.keys || function(obj) {
+ var ret = [];
+ for (var key in obj) ret.push(key);
+ return ret;
+};
+
+/**
+ * Array#forEach shim.
+ */
+
+var forEach = typeof Array.prototype.forEach === 'function'
+ ? function(arr, fn) { return arr.forEach(fn); }
+ : function(arr, fn) {
+ for (var i = 0; i < arr.length; i++) fn(arr[i]);
+ };
+
+/**
+ * Array#reduce shim.
+ */
+
+var reduce = function(arr, fn, initial) {
+ if (typeof arr.reduce === 'function') return arr.reduce(fn, initial);
+ var res = initial;
+ for (var i = 0; i < arr.length; i++) res = fn(res, arr[i]);
+ return res;
+};
+
+/**
+ * Create a nullary object if possible
+ */
+
+function createObject() {
+ return Object.create
+ ? Object.create(null)
+ : {};
+}
+
+/**
* Cache non-integer test regexp.
*/
var isint = /^[0-9]+$/;
function promote(parent, key) {
- if (parent[key].length == 0) return parent[key] = {};
- var t = {};
+ if (parent[key].length == 0) return parent[key] = createObject();
+ var t = createObject();
for (var i in parent[key]) t[i] = parent[key][i];
parent[key] = t;
return t;
var part = parts.shift();
// end
if (!part) {
- if (Array.isArray(parent[key])) {
+ if (isArray(parent[key])) {
parent[key].push(val);
} else if ('object' == typeof parent[key]) {
parent[key] = val;
} else {
var obj = parent[key] = parent[key] || [];
if (']' == part) {
- if (Array.isArray(obj)) {
+ if (isArray(obj)) {
if ('' != val) obj.push(val);
} else if ('object' == typeof obj) {
- obj[Object.keys(obj).length] = val;
+ obj[objectKeys(obj).length] = val;
} else {
obj = parent[key] = [parent[key], val];
}
// prop
- } else if (~part.indexOf(']')) {
+ } else if (~indexOf(part, ']')) {
part = part.substr(0, part.length - 1);
- if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
+ if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
parse(parts, obj, part, val);
// key
} else {
- if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
+ if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
parse(parts, obj, part, val);
}
}
*/
function merge(parent, key, val){
- if (~key.indexOf(']')) {
+ if (~indexOf(key, ']')) {
var parts = key.split('[')
, len = parts.length
, last = len - 1;
parse(parts, parent, 'base', val);
// optimize
} else {
- if (!isint.test(key) && Array.isArray(parent.base)) {
- var t = {};
+ if (!isint.test(key) && isArray(parent.base)) {
+ var t = createObject();
for (var k in parent.base) t[k] = parent.base[k];
parent.base = t;
}
}
/**
+ * Compact sparse arrays.
+ */
+
+function compact(obj) {
+ if ('object' != typeof obj) return obj;
+
+ if (isArray(obj)) {
+ var ret = [];
+ for (var i in obj) ret.push(obj[i]);
+ return ret;
+ }
+
+ for (var key in obj) {
+ obj[key] = compact(obj[key]);
+ }
+
+ return obj;
+}
+
+/**
+ * Restore Object.prototype.
+ * see pull-request #58
+ */
+
+function restoreProto(obj) {
+ if (!Object.create) return obj;
+ if (isArray(obj)) return obj;
+ if (obj && 'object' != typeof obj) return obj;
+
+ for (var key in obj) {
+ if (hasOwnProperty.call(obj, key)) {
+ obj[key] = restoreProto(obj[key]);
+ }
+ }
+
+ obj.__proto__ = Object.prototype;
+ return obj;
+}
+
+/**
* Parse the given obj.
*/
function parseObject(obj){
var ret = { base: {} };
- Object.keys(obj).forEach(function(name){
+
+ forEach(objectKeys(obj), function(name){
merge(ret, name, obj[name]);
});
- return ret.base;
+
+ return compact(ret.base);
}
/**
*/
function parseString(str){
- return String(str)
- .split('&')
- .reduce(function(ret, pair){
- var eql = pair.indexOf('=')
- , brace = lastBraceInKey(pair)
- , key = pair.substr(0, brace || eql)
- , val = pair.substr(brace || eql, pair.length)
- , val = val.substr(val.indexOf('=') + 1, val.length);
-
- // ?foo
- if ('' == key) key = pair, val = '';
-
- return merge(ret, decode(key), decode(val));
- }, { base: {} }).base;
+ var ret = reduce(String(str).split('&'), function(ret, pair){
+ var eql = indexOf(pair, '=')
+ , brace = lastBraceInKey(pair)
+ , key = pair.substr(0, brace || eql)
+ , val = pair.substr(brace || eql, pair.length)
+ , val = val.substr(indexOf(val, '=') + 1, val.length);
+
+ // ?foo
+ if ('' == key) key = pair, val = '';
+ if ('' == key) return ret;
+
+ return merge(ret, decode(key), decode(val));
+ }, { base: createObject() }).base;
+
+ return restoreProto(compact(ret));
}
/**
*/
var stringify = exports.stringify = function(obj, prefix) {
- if (Array.isArray(obj)) {
+ if (isArray(obj)) {
return stringifyArray(obj, prefix);
} else if ('[object Object]' == toString.call(obj)) {
return stringifyObject(obj, prefix);
function stringifyObject(obj, prefix) {
var ret = []
- , keys = Object.keys(obj)
+ , keys = objectKeys(obj)
, key;
for (var i = 0, len = keys.length; i < len; ++i) {
key = keys[i];
+ if ('' == key) continue;
if (null == obj[key]) {
ret.push(encodeURIComponent(key) + '=');
} else {
var v = obj[key];
if (undefined === v) {
obj[key] = val;
- } else if (Array.isArray(v)) {
+ } else if (isArray(v)) {
v.push(val);
} else {
obj[key] = [v, val];
{
"name": "qs",
"description": "querystring parser",
- "version": "0.5.5",
+ "version": "0.6.4",
"keywords": [
"query string",
"parser",
"mocha": "*",
"expect.js": "*"
},
- "component": {
- "scripts": {
- "querystring": "querystring.js"
- }
+ "scripts": {
+ "test": "make test"
},
"author": {
"name": "TJ Holowaychuk",
},
"readme": "# node-querystring\n\n query string parser for node and the browser supporting nesting, as it was removed from `0.3.x`, so this library provides the previous and commonly desired behaviour (and twice as fast). Used by [express](http://expressjs.com), [connect](http://senchalabs.github.com/connect) and others.\n\n## Installation\n\n $ npm install qs\n\n## Examples\n\n```js\nvar qs = require('qs');\n\nqs.parse('user[name][first]=Tobi&user[email]=tobi@learnboost.com');\n// => { user: { name: { first: 'Tobi' }, email: 'tobi@learnboost.com' } }\n\nqs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }})\n// => user[name]=Tobi&user[email]=tobi%40learnboost.com\n```\n\n## Testing\n\nInstall dev dependencies:\n\n $ npm install -d\n\nand execute:\n\n $ make test\n\nbrowser:\n\n $ open test/browser/index.html\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"readmeFilename": "Readme.md",
- "_id": "qs@0.5.5",
- "dist": {
- "shasum": "e267764dfdc9cf1ec5682f1038d39e0a0af2bc5d"
+ "bugs": {
+ "url": "https://github.com/visionmedia/node-querystring/issues"
},
- "_from": "qs@~0.5.4",
- "_resolved": "https://registry.npmjs.org/qs/-/qs-0.5.5.tgz"
+ "_id": "qs@0.6.4",
+ "_from": "qs@~0.6.0"
}
+++ /dev/null
-
-(function (global, module) {
-
- if ('undefined' == typeof module) {
- var module = { exports: {} }
- , exports = module.exports
- }
-
- /**
- * Exports.
- */
-
- module.exports = expect;
- expect.Assertion = Assertion;
-
- /**
- * Exports version.
- */
-
- expect.version = '0.1.2';
-
- /**
- * Possible assertion flags.
- */
-
- var flags = {
- not: ['to', 'be', 'have', 'include', 'only']
- , to: ['be', 'have', 'include', 'only', 'not']
- , only: ['have']
- , have: ['own']
- , be: ['an']
- };
-
- function expect (obj) {
- return new Assertion(obj);
- }
-
- /**
- * Constructor
- *
- * @api private
- */
-
- function Assertion (obj, flag, parent) {
- this.obj = obj;
- this.flags = {};
-
- if (undefined != parent) {
- this.flags[flag] = true;
-
- for (var i in parent.flags) {
- if (parent.flags.hasOwnProperty(i)) {
- this.flags[i] = true;
- }
- }
- }
-
- var $flags = flag ? flags[flag] : keys(flags)
- , self = this
-
- if ($flags) {
- for (var i = 0, l = $flags.length; i < l; i++) {
- // avoid recursion
- if (this.flags[$flags[i]]) continue;
-
- var name = $flags[i]
- , assertion = new Assertion(this.obj, name, this)
-
- if ('function' == typeof Assertion.prototype[name]) {
- // clone the function, make sure we dont touch the prot reference
- var old = this[name];
- this[name] = function () {
- return old.apply(self, arguments);
- }
-
- for (var fn in Assertion.prototype) {
- if (Assertion.prototype.hasOwnProperty(fn) && fn != name) {
- this[name][fn] = bind(assertion[fn], assertion);
- }
- }
- } else {
- this[name] = assertion;
- }
- }
- }
- };
-
- /**
- * Performs an assertion
- *
- * @api private
- */
-
- Assertion.prototype.assert = function (truth, msg, error) {
- var msg = this.flags.not ? error : msg
- , ok = this.flags.not ? !truth : truth;
-
- if (!ok) {
- throw new Error(msg);
- }
-
- this.and = new Assertion(this.obj);
- };
-
- /**
- * Check if the value is truthy
- *
- * @api public
- */
-
- Assertion.prototype.ok = function () {
- this.assert(
- !!this.obj
- , 'expected ' + i(this.obj) + ' to be truthy'
- , 'expected ' + i(this.obj) + ' to be falsy');
- };
-
- /**
- * Assert that the function throws.
- *
- * @param {Function|RegExp} callback, or regexp to match error string against
- * @api public
- */
-
- Assertion.prototype.throwError =
- Assertion.prototype.throwException = function (fn) {
- expect(this.obj).to.be.a('function');
-
- var thrown = false
- , not = this.flags.not
-
- try {
- this.obj();
- } catch (e) {
- if ('function' == typeof fn) {
- fn(e);
- } else if ('object' == typeof fn) {
- var subject = 'string' == typeof e ? e : e.message;
- if (not) {
- expect(subject).to.not.match(fn);
- } else {
- expect(subject).to.match(fn);
- }
- }
- thrown = true;
- }
-
- if ('object' == typeof fn && not) {
- // in the presence of a matcher, ensure the `not` only applies to
- // the matching.
- this.flags.not = false;
- }
-
- var name = this.obj.name || 'fn';
- this.assert(
- thrown
- , 'expected ' + name + ' to throw an exception'
- , 'expected ' + name + ' not to throw an exception');
- };
-
- /**
- * Checks if the array is empty.
- *
- * @api public
- */
-
- Assertion.prototype.empty = function () {
- var expectation;
-
- if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) {
- if ('number' == typeof this.obj.length) {
- expectation = !this.obj.length;
- } else {
- expectation = !keys(this.obj).length;
- }
- } else {
- if ('string' != typeof this.obj) {
- expect(this.obj).to.be.an('object');
- }
-
- expect(this.obj).to.have.property('length');
- expectation = !this.obj.length;
- }
-
- this.assert(
- expectation
- , 'expected ' + i(this.obj) + ' to be empty'
- , 'expected ' + i(this.obj) + ' to not be empty');
- return this;
- };
-
- /**
- * Checks if the obj exactly equals another.
- *
- * @api public
- */
-
- Assertion.prototype.be =
- Assertion.prototype.equal = function (obj) {
- this.assert(
- obj === this.obj
- , 'expected ' + i(this.obj) + ' to equal ' + i(obj)
- , 'expected ' + i(this.obj) + ' to not equal ' + i(obj));
- return this;
- };
-
- /**
- * Checks if the obj sortof equals another.
- *
- * @api public
- */
-
- Assertion.prototype.eql = function (obj) {
- this.assert(
- expect.eql(obj, this.obj)
- , 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj)
- , 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj));
- return this;
- };
-
- /**
- * Assert within start to finish (inclusive).
- *
- * @param {Number} start
- * @param {Number} finish
- * @api public
- */
-
- Assertion.prototype.within = function (start, finish) {
- var range = start + '..' + finish;
- this.assert(
- this.obj >= start && this.obj <= finish
- , 'expected ' + i(this.obj) + ' to be within ' + range
- , 'expected ' + i(this.obj) + ' to not be within ' + range);
- return this;
- };
-
- /**
- * Assert typeof / instance of
- *
- * @api public
- */
-
- Assertion.prototype.a =
- Assertion.prototype.an = function (type) {
- if ('string' == typeof type) {
- // proper english in error msg
- var n = /^[aeiou]/.test(type) ? 'n' : '';
-
- // typeof with support for 'array'
- this.assert(
- 'array' == type ? isArray(this.obj) :
- 'object' == type
- ? 'object' == typeof this.obj && null !== this.obj
- : type == typeof this.obj
- , 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type
- , 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type);
- } else {
- // instanceof
- var name = type.name || 'supplied constructor';
- this.assert(
- this.obj instanceof type
- , 'expected ' + i(this.obj) + ' to be an instance of ' + name
- , 'expected ' + i(this.obj) + ' not to be an instance of ' + name);
- }
-
- return this;
- };
-
- /**
- * Assert numeric value above _n_.
- *
- * @param {Number} n
- * @api public
- */
-
- Assertion.prototype.greaterThan =
- Assertion.prototype.above = function (n) {
- this.assert(
- this.obj > n
- , 'expected ' + i(this.obj) + ' to be above ' + n
- , 'expected ' + i(this.obj) + ' to be below ' + n);
- return this;
- };
-
- /**
- * Assert numeric value below _n_.
- *
- * @param {Number} n
- * @api public
- */
-
- Assertion.prototype.lessThan =
- Assertion.prototype.below = function (n) {
- this.assert(
- this.obj < n
- , 'expected ' + i(this.obj) + ' to be below ' + n
- , 'expected ' + i(this.obj) + ' to be above ' + n);
- return this;
- };
-
- /**
- * Assert string value matches _regexp_.
- *
- * @param {RegExp} regexp
- * @api public
- */
-
- Assertion.prototype.match = function (regexp) {
- this.assert(
- regexp.exec(this.obj)
- , 'expected ' + i(this.obj) + ' to match ' + regexp
- , 'expected ' + i(this.obj) + ' not to match ' + regexp);
- return this;
- };
-
- /**
- * Assert property "length" exists and has value of _n_.
- *
- * @param {Number} n
- * @api public
- */
-
- Assertion.prototype.length = function (n) {
- expect(this.obj).to.have.property('length');
- var len = this.obj.length;
- this.assert(
- n == len
- , 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len
- , 'expected ' + i(this.obj) + ' to not have a length of ' + len);
- return this;
- };
-
- /**
- * Assert property _name_ exists, with optional _val_.
- *
- * @param {String} name
- * @param {Mixed} val
- * @api public
- */
-
- Assertion.prototype.property = function (name, val) {
- if (this.flags.own) {
- this.assert(
- Object.prototype.hasOwnProperty.call(this.obj, name)
- , 'expected ' + i(this.obj) + ' to have own property ' + i(name)
- , 'expected ' + i(this.obj) + ' to not have own property ' + i(name));
- return this;
- }
-
- if (this.flags.not && undefined !== val) {
- if (undefined === this.obj[name]) {
- throw new Error(i(this.obj) + ' has no property ' + i(name));
- }
- } else {
- var hasProp;
- try {
- hasProp = name in this.obj
- } catch (e) {
- hasProp = undefined !== this.obj[name]
- }
-
- this.assert(
- hasProp
- , 'expected ' + i(this.obj) + ' to have a property ' + i(name)
- , 'expected ' + i(this.obj) + ' to not have a property ' + i(name));
- }
-
- if (undefined !== val) {
- this.assert(
- val === this.obj[name]
- , 'expected ' + i(this.obj) + ' to have a property ' + i(name)
- + ' of ' + i(val) + ', but got ' + i(this.obj[name])
- , 'expected ' + i(this.obj) + ' to not have a property ' + i(name)
- + ' of ' + i(val));
- }
-
- this.obj = this.obj[name];
- return this;
- };
-
- /**
- * Assert that the array contains _obj_ or string contains _obj_.
- *
- * @param {Mixed} obj|string
- * @api public
- */
-
- Assertion.prototype.string =
- Assertion.prototype.contain = function (obj) {
- if ('string' == typeof this.obj) {
- this.assert(
- ~this.obj.indexOf(obj)
- , 'expected ' + i(this.obj) + ' to contain ' + i(obj)
- , 'expected ' + i(this.obj) + ' to not contain ' + i(obj));
- } else {
- this.assert(
- ~indexOf(this.obj, obj)
- , 'expected ' + i(this.obj) + ' to contain ' + i(obj)
- , 'expected ' + i(this.obj) + ' to not contain ' + i(obj));
- }
- return this;
- };
-
- /**
- * Assert exact keys or inclusion of keys by using
- * the `.own` modifier.
- *
- * @param {Array|String ...} keys
- * @api public
- */
-
- Assertion.prototype.key =
- Assertion.prototype.keys = function ($keys) {
- var str
- , ok = true;
-
- $keys = isArray($keys)
- ? $keys
- : Array.prototype.slice.call(arguments);
-
- if (!$keys.length) throw new Error('keys required');
-
- var actual = keys(this.obj)
- , len = $keys.length;
-
- // Inclusion
- ok = every($keys, function (key) {
- return ~indexOf(actual, key);
- });
-
- // Strict
- if (!this.flags.not && this.flags.only) {
- ok = ok && $keys.length == actual.length;
- }
-
- // Key string
- if (len > 1) {
- $keys = map($keys, function (key) {
- return i(key);
- });
- var last = $keys.pop();
- str = $keys.join(', ') + ', and ' + last;
- } else {
- str = i($keys[0]);
- }
-
- // Form
- str = (len > 1 ? 'keys ' : 'key ') + str;
-
- // Have / include
- str = (!this.flags.only ? 'include ' : 'only have ') + str;
-
- // Assertion
- this.assert(
- ok
- , 'expected ' + i(this.obj) + ' to ' + str
- , 'expected ' + i(this.obj) + ' to not ' + str);
-
- return this;
- };
-
- /**
- * Function bind implementation.
- */
-
- function bind (fn, scope) {
- return function () {
- return fn.apply(scope, arguments);
- }
- }
-
- /**
- * Array every compatibility
- *
- * @see bit.ly/5Fq1N2
- * @api public
- */
-
- function every (arr, fn, thisObj) {
- var scope = thisObj || global;
- for (var i = 0, j = arr.length; i < j; ++i) {
- if (!fn.call(scope, arr[i], i, arr)) {
- return false;
- }
- }
- return true;
- };
-
- /**
- * Array indexOf compatibility.
- *
- * @see bit.ly/a5Dxa2
- * @api public
- */
-
- function indexOf (arr, o, i) {
- if (Array.prototype.indexOf) {
- return Array.prototype.indexOf.call(arr, o, i);
- }
-
- if (arr.length === undefined) {
- return -1;
- }
-
- for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0
- ; i < j && arr[i] !== o; i++);
-
- return j <= i ? -1 : i;
- };
-
- /**
- * Inspects an object.
- *
- * @see taken from node.js `util` module (copyright Joyent, MIT license)
- * @api private
- */
-
- function i (obj, showHidden, depth) {
- var seen = [];
-
- function stylize (str) {
- return str;
- };
-
- function format (value, recurseTimes) {
- // Provide a hook for user-specified inspect functions.
- // Check that value is an object with an inspect function on it
- if (value && typeof value.inspect === 'function' &&
- // Filter out the util module, it's inspect function is special
- value !== exports &&
- // Also filter out any prototype objects using the circular check.
- !(value.constructor && value.constructor.prototype === value)) {
- return value.inspect(recurseTimes);
- }
-
- // Primitive types cannot have properties
- switch (typeof value) {
- case 'undefined':
- return stylize('undefined', 'undefined');
-
- case 'string':
- var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '')
- .replace(/'/g, "\\'")
- .replace(/\\"/g, '"') + '\'';
- return stylize(simple, 'string');
-
- case 'number':
- return stylize('' + value, 'number');
-
- case 'boolean':
- return stylize('' + value, 'boolean');
- }
- // For some reason typeof null is "object", so special case here.
- if (value === null) {
- return stylize('null', 'null');
- }
-
- // Look up the keys of the object.
- var visible_keys = keys(value);
- var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys;
-
- // Functions without properties can be shortcutted.
- if (typeof value === 'function' && $keys.length === 0) {
- if (isRegExp(value)) {
- return stylize('' + value, 'regexp');
- } else {
- var name = value.name ? ': ' + value.name : '';
- return stylize('[Function' + name + ']', 'special');
- }
- }
-
- // Dates without properties can be shortcutted
- if (isDate(value) && $keys.length === 0) {
- return stylize(value.toUTCString(), 'date');
- }
-
- var base, type, braces;
- // Determine the object type
- if (isArray(value)) {
- type = 'Array';
- braces = ['[', ']'];
- } else {
- type = 'Object';
- braces = ['{', '}'];
- }
-
- // Make functions say that they are functions
- if (typeof value === 'function') {
- var n = value.name ? ': ' + value.name : '';
- base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
- } else {
- base = '';
- }
-
- // Make dates with properties first say the date
- if (isDate(value)) {
- base = ' ' + value.toUTCString();
- }
-
- if ($keys.length === 0) {
- return braces[0] + base + braces[1];
- }
-
- if (recurseTimes < 0) {
- if (isRegExp(value)) {
- return stylize('' + value, 'regexp');
- } else {
- return stylize('[Object]', 'special');
- }
- }
-
- seen.push(value);
-
- var output = map($keys, function (key) {
- var name, str;
- if (value.__lookupGetter__) {
- if (value.__lookupGetter__(key)) {
- if (value.__lookupSetter__(key)) {
- str = stylize('[Getter/Setter]', 'special');
- } else {
- str = stylize('[Getter]', 'special');
- }
- } else {
- if (value.__lookupSetter__(key)) {
- str = stylize('[Setter]', 'special');
- }
- }
- }
- if (indexOf(visible_keys, key) < 0) {
- name = '[' + key + ']';
- }
- if (!str) {
- if (indexOf(seen, value[key]) < 0) {
- if (recurseTimes === null) {
- str = format(value[key]);
- } else {
- str = format(value[key], recurseTimes - 1);
- }
- if (str.indexOf('\n') > -1) {
- if (isArray(value)) {
- str = map(str.split('\n'), function (line) {
- return ' ' + line;
- }).join('\n').substr(2);
- } else {
- str = '\n' + map(str.split('\n'), function (line) {
- return ' ' + line;
- }).join('\n');
- }
- }
- } else {
- str = stylize('[Circular]', 'special');
- }
- }
- if (typeof name === 'undefined') {
- if (type === '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 = stylize(name, 'name');
- } else {
- name = name.replace(/'/g, "\\'")
- .replace(/\\"/g, '"')
- .replace(/(^"|"$)/g, "'");
- name = stylize(name, 'string');
- }
- }
-
- return name + ': ' + str;
- });
-
- seen.pop();
-
- var numLinesEst = 0;
- var length = reduce(output, function (prev, cur) {
- numLinesEst++;
- if (indexOf(cur, '\n') >= 0) numLinesEst++;
- return prev + cur.length + 1;
- }, 0);
-
- if (length > 50) {
- output = braces[0] +
- (base === '' ? '' : base + '\n ') +
- ' ' +
- output.join(',\n ') +
- ' ' +
- braces[1];
-
- } else {
- output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
- }
-
- return output;
- }
- return format(obj, (typeof depth === 'undefined' ? 2 : depth));
- };
-
- function isArray (ar) {
- return Object.prototype.toString.call(ar) == '[object Array]';
- };
-
- function isRegExp(re) {
- var s = '' + re;
- return re instanceof RegExp || // easy case
- // duck-type for context-switching evalcx case
- typeof(re) === 'function' &&
- re.constructor.name === 'RegExp' &&
- re.compile &&
- re.test &&
- re.exec &&
- s.match(/^\/.*\/[gim]{0,3}$/);
- };
-
- function isDate(d) {
- if (d instanceof Date) return true;
- return false;
- };
-
- function keys (obj) {
- if (Object.keys) {
- return Object.keys(obj);
- }
-
- var keys = [];
-
- for (var i in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, i)) {
- keys.push(i);
- }
- }
-
- return keys;
- }
-
- function map (arr, mapper, that) {
- if (Array.prototype.map) {
- return Array.prototype.map.call(arr, mapper, that);
- }
-
- var other= new Array(arr.length);
-
- for (var i= 0, n = arr.length; i<n; i++)
- if (i in arr)
- other[i] = mapper.call(that, arr[i], i, arr);
-
- return other;
- };
-
- function reduce (arr, fun) {
- if (Array.prototype.reduce) {
- return Array.prototype.reduce.apply(
- arr
- , Array.prototype.slice.call(arguments, 1)
- );
- }
-
- var len = +this.length;
-
- if (typeof fun !== "function")
- throw new TypeError();
-
- // no value to return if no initial value and an empty array
- if (len === 0 && arguments.length === 1)
- throw new TypeError();
-
- var i = 0;
- if (arguments.length >= 2) {
- var rv = arguments[1];
- } else {
- do {
- if (i in this) {
- rv = this[i++];
- break;
- }
-
- // if array contains no values, no initial value to return
- if (++i >= len)
- throw new TypeError();
- } while (true);
- }
-
- for (; i < len; i++) {
- if (i in this)
- rv = fun.call(null, rv, this[i], i, this);
- }
-
- return rv;
- };
-
- /**
- * Asserts deep equality
- *
- * @see taken from node.js `assert` module (copyright Joyent, MIT license)
- * @api private
- */
-
- expect.eql = function eql (actual, expected) {
- // 7.1. All identical values are equivalent, as determined by ===.
- if (actual === expected) {
- return true;
- } else if ('undefined' != typeof Buffer
- && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
- if (actual.length != expected.length) return false;
-
- for (var i = 0; i < actual.length; i++) {
- if (actual[i] !== expected[i]) return false;
- }
-
- return true;
-
- // 7.2. If the expected value is a Date object, the actual value is
- // equivalent if it is also a Date object that refers to the same time.
- } else if (actual instanceof Date && expected instanceof Date) {
- return actual.getTime() === expected.getTime();
-
- // 7.3. Other pairs that do not both pass typeof value == "object",
- // equivalence is determined by ==.
- } else if (typeof actual != 'object' && typeof expected != 'object') {
- return actual == expected;
-
- // 7.4. For all other Object pairs, including Array objects, equivalence is
- // determined by having the same number of owned properties (as verified
- // with Object.prototype.hasOwnProperty.call), the same set of keys
- // (although not necessarily the same order), equivalent values for every
- // corresponding key, and an identical "prototype" property. Note: this
- // accounts for both named and indexed properties on Arrays.
- } else {
- return objEquiv(actual, expected);
- }
- }
-
- function isUndefinedOrNull (value) {
- return value === null || value === undefined;
- }
-
- function isArguments (object) {
- return Object.prototype.toString.call(object) == '[object Arguments]';
- }
-
- function objEquiv (a, b) {
- if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
- return false;
- // an identical "prototype" property.
- if (a.prototype !== b.prototype) return false;
- //~~~I've managed to break Object.keys through screwy arguments passing.
- // Converting to array solves the problem.
- if (isArguments(a)) {
- if (!isArguments(b)) {
- return false;
- }
- a = pSlice.call(a);
- b = pSlice.call(b);
- return expect.eql(a, b);
- }
- try{
- var ka = keys(a),
- kb = keys(b),
- key, i;
- } catch (e) {//happens when one is a string literal and the other isn't
- return false;
- }
- // having the same number of owned properties (keys incorporates hasOwnProperty)
- if (ka.length != kb.length)
- return false;
- //the same set of keys (although not necessarily the same order),
- ka.sort();
- kb.sort();
- //~~~cheap key test
- for (i = ka.length - 1; i >= 0; i--) {
- if (ka[i] != kb[i])
- return false;
- }
- //equivalent values for every corresponding key, and
- //~~~possibly expensive deep test
- for (i = ka.length - 1; i >= 0; i--) {
- key = ka[i];
- if (!expect.eql(a[key], b[key]))
- return false;
- }
- return true;
- }
-
- var json = (function () {
- "use strict";
-
- if ('object' == typeof JSON && JSON.parse && JSON.stringify) {
- return {
- parse: nativeJSON.parse
- , stringify: nativeJSON.stringify
- }
- }
-
- var JSON = {};
-
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- function date(d, key) {
- return isFinite(d.valueOf()) ?
- d.getUTCFullYear() + '-' +
- f(d.getUTCMonth() + 1) + '-' +
- f(d.getUTCDate()) + 'T' +
- f(d.getUTCHours()) + ':' +
- f(d.getUTCMinutes()) + ':' +
- f(d.getUTCSeconds()) + 'Z' : null;
- };
-
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- gap,
- indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
- rep;
-
-
- function quote(string) {
-
- // If the string contains no control characters, no quote characters, and no
- // backslash characters, then we can safely slap some quotes around it.
- // Otherwise we must also replace the offending characters with safe escape
- // sequences.
-
- escapable.lastIndex = 0;
- return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string' ? c :
- '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' : '"' + string + '"';
- }
-
-
- function str(key, holder) {
-
- // Produce a string from holder[key].
-
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
-
- // If the value has a toJSON method, call it to obtain a replacement value.
-
- if (value instanceof Date) {
- value = date(key);
- }
-
- // If we were called with a replacer function, then call the replacer to
- // obtain a replacement value.
-
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
-
- // What happens next depends on the value's type.
-
- switch (typeof value) {
- case 'string':
- return quote(value);
-
- case 'number':
-
- // JSON numbers must be finite. Encode non-finite numbers as null.
-
- return isFinite(value) ? String(value) : 'null';
-
- case 'boolean':
- case 'null':
-
- // If the value is a boolean or null, convert it to a string. Note:
- // typeof null does not produce 'null'. The case is included here in
- // the remote chance that this gets fixed someday.
-
- return String(value);
-
- // If the type is 'object', we might be dealing with an object or an array or
- // null.
-
- case 'object':
-
- // Due to a specification blunder in ECMAScript, typeof null is 'object',
- // so watch out for that case.
-
- if (!value) {
- return 'null';
- }
-
- // Make an array to hold the partial results of stringifying this object value.
-
- gap += indent;
- partial = [];
-
- // Is the value an array?
-
- if (Object.prototype.toString.apply(value) === '[object Array]') {
-
- // The value is an array. Stringify every element. Use null as a placeholder
- // for non-JSON values.
-
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
-
- // Join all of the elements together, separated with commas, and wrap them in
- // brackets.
-
- v = partial.length === 0 ? '[]' : gap ?
- '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
- '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
-
- // If the replacer is an array, use it to select the members to be stringified.
-
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- if (typeof rep[i] === 'string') {
- k = rep[i];
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- } else {
-
- // Otherwise, iterate through all of the keys in the object.
-
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
-
- // Join all of the member texts together, separated with commas,
- // and wrap them in braces.
-
- v = partial.length === 0 ? '{}' : gap ?
- '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
- '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
- }
-
- // If the JSON object does not yet have a stringify method, give it one.
-
- JSON.stringify = function (value, replacer, space) {
-
- // The stringify method takes a value and an optional replacer, and an optional
- // space parameter, and returns a JSON text. The replacer can be a function
- // that can replace values, or an array of strings that will select the keys.
- // A default replacer method can be provided. Use of the space parameter can
- // produce text that is more easily readable.
-
- var i;
- gap = '';
- indent = '';
-
- // If the space parameter is a number, make an indent string containing that
- // many spaces.
-
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
-
- // If the space parameter is a string, it will be used as the indent string.
-
- } else if (typeof space === 'string') {
- indent = space;
- }
-
- // If there is a replacer, it must be a function or an array.
- // Otherwise, throw an error.
-
- rep = replacer;
- if (replacer && typeof replacer !== 'function' &&
- (typeof replacer !== 'object' ||
- typeof replacer.length !== 'number')) {
- throw new Error('JSON.stringify');
- }
-
- // Make a fake root object containing our value under the key of ''.
- // Return the result of stringifying the value.
-
- return str('', {'': value});
- };
-
- // If the JSON object does not yet have a parse method, give it one.
-
- JSON.parse = function (text, reviver) {
- // The parse method takes a text and an optional reviver function, and returns
- // a JavaScript value if the text is a valid JSON text.
-
- var j;
-
- function walk(holder, key) {
-
- // The walk method is used to recursively walk the resulting structure so
- // that modifications can be made.
-
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }
-
-
- // Parsing happens in four stages. In the first stage, we replace certain
- // Unicode characters with escape sequences. JavaScript handles many characters
- // incorrectly, either silently deleting them, or treating them as line endings.
-
- text = String(text);
- cx.lastIndex = 0;
- if (cx.test(text)) {
- text = text.replace(cx, function (a) {
- return '\\u' +
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- });
- }
-
- // In the second stage, we run the text against regular expressions that look
- // for non-JSON patterns. We are especially concerned with '()' and 'new'
- // because they can cause invocation, and '=' because it can cause mutation.
- // But just to be safe, we want to reject all unexpected forms.
-
- // We split the second stage into 4 regexp operations in order to work around
- // crippling inefficiencies in IE's and Safari's regexp engines. First we
- // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
- // replace all simple value tokens with ']' characters. Third, we delete all
- // open brackets that follow a colon or comma or that begin the text. Finally,
- // we look to see that the remaining characters are only whitespace or ']' or
- // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
- if (/^[\],:{}\s]*$/
- .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
- .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
- .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
- // In the third stage we use the eval function to compile the text into a
- // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
- // in JavaScript: it can begin a block or an object literal. We wrap the text
- // in parens to eliminate the ambiguity.
-
- j = eval('(' + text + ')');
-
- // In the optional fourth stage, we recursively walk the new structure, passing
- // each name/value pair to a reviver function for possible transformation.
-
- return typeof reviver === 'function' ?
- walk({'': j}, '') : j;
- }
-
- // If the text is not JSON parseable, then a SyntaxError is thrown.
-
- throw new SyntaxError('JSON.parse');
- };
-
- return JSON;
- })();
-
- if ('undefined' != typeof window) {
- window.expect = module.exports;
- }
-
-})(
- this
- , 'undefined' != typeof module ? module : {}
- , 'undefined' != typeof exports ? exports : {}
-);
\ No newline at end of file
+++ /dev/null
-<html>
- <head>
- <title>Mocha</title>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <link rel="stylesheet" href="mocha.css" />
- <script src="jquery.js" type="text/javascript"></script>
- <script src="expect.js"></script>
- <script src="mocha.js"></script>
- <script>mocha.setup('bdd')</script>
- <script src="qs.js"></script>
- <script src="../parse.js"></script>
- <script src="../stringify.js"></script>
- <script>onload = mocha.run;</script>
- </head>
- <body>
- <div id="mocha"></div>
- </body>
-</html>
+++ /dev/null
-/*!
- * jQuery JavaScript Library v1.6.2
- * http://jquery.com/
- *
- * Copyright 2011, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2011, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Thu Jun 30 14:16:56 2011 -0400
- */
-(function( window, undefined ) {
-
-// Use the correct document accordingly with window argument (sandbox)
-var document = window.document,
- navigator = window.navigator,
- location = window.location;
-var jQuery = (function() {
-
-// Define a local copy of jQuery
-var jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- },
-
- // Map over jQuery in case of overwrite
- _jQuery = window.jQuery,
-
- // Map over the $ in case of overwrite
- _$ = window.$,
-
- // A central reference to the root jQuery(document)
- rootjQuery,
-
- // A simple way to check for HTML strings or ID strings
- // (both of which we optimize for)
- quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
-
- // Check if a string has a non-whitespace character in it
- rnotwhite = /\S/,
-
- // Used for trimming whitespace
- trimLeft = /^\s+/,
- trimRight = /\s+$/,
-
- // Check for digits
- rdigit = /\d/,
-
- // Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
-
- // JSON RegExp
- rvalidchars = /^[\],:{}\s]*$/,
- rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
- rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
- rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
-
- // Useragent RegExp
- rwebkit = /(webkit)[ \/]([\w.]+)/,
- ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
- rmsie = /(msie) ([\w.]+)/,
- rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
-
- // Matches dashed string for camelizing
- rdashAlpha = /-([a-z])/ig,
-
- // Used by jQuery.camelCase as callback to replace()
- fcamelCase = function( all, letter ) {
- return letter.toUpperCase();
- },
-
- // Keep a UserAgent string for use with jQuery.browser
- userAgent = navigator.userAgent,
-
- // For matching the engine and version of the browser
- browserMatch,
-
- // The deferred used on DOM ready
- readyList,
-
- // The ready event handler
- DOMContentLoaded,
-
- // Save a reference to some core methods
- toString = Object.prototype.toString,
- hasOwn = Object.prototype.hasOwnProperty,
- push = Array.prototype.push,
- slice = Array.prototype.slice,
- trim = String.prototype.trim,
- indexOf = Array.prototype.indexOf,
-
- // [[Class]] -> type pairs
- class2type = {};
-
-jQuery.fn = jQuery.prototype = {
- constructor: jQuery,
- init: function( selector, context, rootjQuery ) {
- var match, elem, ret, doc;
-
- // Handle $(""), $(null), or $(undefined)
- if ( !selector ) {
- return this;
- }
-
- // Handle $(DOMElement)
- if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
- }
-
- // The body element only exists once, optimize finding it
- if ( selector === "body" && !context && document.body ) {
- this.context = document;
- this[0] = document.body;
- this.selector = selector;
- this.length = 1;
- return this;
- }
-
- // Handle HTML strings
- if ( typeof selector === "string" ) {
- // Are we dealing with HTML string or an ID?
- if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
- // Assume that strings that start and end with <> are HTML and skip the regex check
- match = [ null, selector, null ];
-
- } else {
- match = quickExpr.exec( selector );
- }
-
- // Verify a match, and that no context was specified for #id
- if ( match && (match[1] || !context) ) {
-
- // HANDLE: $(html) -> $(array)
- if ( match[1] ) {
- context = context instanceof jQuery ? context[0] : context;
- doc = (context ? context.ownerDocument || context : document);
-
- // If a single string is passed in and it's a single tag
- // just do a createElement and skip the rest
- ret = rsingleTag.exec( selector );
-
- if ( ret ) {
- if ( jQuery.isPlainObject( context ) ) {
- selector = [ document.createElement( ret[1] ) ];
- jQuery.fn.attr.call( selector, context, true );
-
- } else {
- selector = [ doc.createElement( ret[1] ) ];
- }
-
- } else {
- ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
- selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
- }
-
- return jQuery.merge( this, selector );
-
- // HANDLE: $("#id")
- } else {
- elem = document.getElementById( match[2] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id !== match[2] ) {
- return rootjQuery.find( selector );
- }
-
- // Otherwise, we inject the element directly into the jQuery object
- this.length = 1;
- this[0] = elem;
- }
-
- this.context = document;
- this.selector = selector;
- return this;
- }
-
- // HANDLE: $(expr, $(...))
- } else if ( !context || context.jquery ) {
- return (context || rootjQuery).find( selector );
-
- // HANDLE: $(expr, context)
- // (which is just equivalent to: $(context).find(expr)
- } else {
- return this.constructor( context ).find( selector );
- }
-
- // HANDLE: $(function)
- // Shortcut for document ready
- } else if ( jQuery.isFunction( selector ) ) {
- return rootjQuery.ready( selector );
- }
-
- if (selector.selector !== undefined) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
-
- return jQuery.makeArray( selector, this );
- },
-
- // Start with an empty selector
- selector: "",
-
- // The current version of jQuery being used
- jquery: "1.6.2",
-
- // The default length of a jQuery object is 0
- length: 0,
-
- // The number of elements contained in the matched element set
- size: function() {
- return this.length;
- },
-
- toArray: function() {
- return slice.call( this, 0 );
- },
-
- // Get the Nth element in the matched element set OR
- // Get the whole matched element set as a clean array
- get: function( num ) {
- return num == null ?
-
- // Return a 'clean' array
- this.toArray() :
-
- // Return just the object
- ( num < 0 ? this[ this.length + num ] : this[ num ] );
- },
-
- // Take an array of elements and push it onto the stack
- // (returning the new matched element set)
- pushStack: function( elems, name, selector ) {
- // Build a new jQuery matched element set
- var ret = this.constructor();
-
- if ( jQuery.isArray( elems ) ) {
- push.apply( ret, elems );
-
- } else {
- jQuery.merge( ret, elems );
- }
-
- // Add the old object onto the stack (as a reference)
- ret.prevObject = this;
-
- ret.context = this.context;
-
- if ( name === "find" ) {
- ret.selector = this.selector + (this.selector ? " " : "") + selector;
- } else if ( name ) {
- ret.selector = this.selector + "." + name + "(" + selector + ")";
- }
-
- // Return the newly-formed element set
- return ret;
- },
-
- // Execute a callback for every element in the matched set.
- // (You can seed the arguments with an array of args, but this is
- // only used internally.)
- each: function( callback, args ) {
- return jQuery.each( this, callback, args );
- },
-
- ready: function( fn ) {
- // Attach the listeners
- jQuery.bindReady();
-
- // Add the callback
- readyList.done( fn );
-
- return this;
- },
-
- eq: function( i ) {
- return i === -1 ?
- this.slice( i ) :
- this.slice( i, +i + 1 );
- },
-
- first: function() {
- return this.eq( 0 );
- },
-
- last: function() {
- return this.eq( -1 );
- },
-
- slice: function() {
- return this.pushStack( slice.apply( this, arguments ),
- "slice", slice.call(arguments).join(",") );
- },
-
- map: function( callback ) {
- return this.pushStack( jQuery.map(this, function( elem, i ) {
- return callback.call( elem, i, elem );
- }));
- },
-
- end: function() {
- return this.prevObject || this.constructor(null);
- },
-
- // For internal use only.
- // Behaves like an Array's method, not like a jQuery method.
- push: push,
- sort: [].sort,
- splice: [].splice
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.fn.init.prototype = jQuery.fn;
-
-jQuery.extend = jQuery.fn.extend = function() {
- var options, name, src, copy, copyIsArray, clone,
- target = arguments[0] || {},
- i = 1,
- length = arguments.length,
- deep = false;
-
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[1] || {};
- // skip the boolean and the target
- i = 2;
- }
-
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
- target = {};
- }
-
- // extend jQuery itself if only one argument is passed
- if ( length === i ) {
- target = this;
- --i;
- }
-
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
-
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
-
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && jQuery.isArray(src) ? src : [];
-
- } else {
- clone = src && jQuery.isPlainObject(src) ? src : {};
- }
-
- // Never move original objects, clone them
- target[ name ] = jQuery.extend( deep, clone, copy );
-
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
-
- // Return the modified object
- return target;
-};
-
-jQuery.extend({
- noConflict: function( deep ) {
- if ( window.$ === jQuery ) {
- window.$ = _$;
- }
-
- if ( deep && window.jQuery === jQuery ) {
- window.jQuery = _jQuery;
- }
-
- return jQuery;
- },
-
- // Is the DOM ready to be used? Set to true once it occurs.
- isReady: false,
-
- // A counter to track how many items to wait for before
- // the ready event fires. See #6781
- readyWait: 1,
-
- // Hold (or release) the ready event
- holdReady: function( hold ) {
- if ( hold ) {
- jQuery.readyWait++;
- } else {
- jQuery.ready( true );
- }
- },
-
- // Handle when the DOM is ready
- ready: function( wait ) {
- // Either a released hold or an DOMready/load event and not yet ready
- if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready, 1 );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger( "ready" ).unbind( "ready" );
- }
- }
- },
-
- bindReady: function() {
- if ( readyList ) {
- return;
- }
-
- readyList = jQuery._Deferred();
-
- // Catch cases where $(document).ready() is called after the
- // browser event has already occurred.
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
- return setTimeout( jQuery.ready, 1 );
- }
-
- // Mozilla, Opera and webkit nightlies currently support this event
- if ( document.addEventListener ) {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
-
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", jQuery.ready, false );
-
- // If IE event model is used
- } else if ( document.attachEvent ) {
- // ensure firing before onload,
- // maybe late but safe also for iframes
- document.attachEvent( "onreadystatechange", DOMContentLoaded );
-
- // A fallback to window.onload, that will always work
- window.attachEvent( "onload", jQuery.ready );
-
- // If IE and not a frame
- // continually check to see if the document is ready
- var toplevel = false;
-
- try {
- toplevel = window.frameElement == null;
- } catch(e) {}
-
- if ( document.documentElement.doScroll && toplevel ) {
- doScrollCheck();
- }
- }
- },
-
- // See test/unit/core.js for details concerning isFunction.
- // Since version 1.3, DOM methods and functions like alert
- // aren't supported. They return false on IE (#2968).
- isFunction: function( obj ) {
- return jQuery.type(obj) === "function";
- },
-
- isArray: Array.isArray || function( obj ) {
- return jQuery.type(obj) === "array";
- },
-
- // A crude way of determining if an object is a window
- isWindow: function( obj ) {
- return obj && typeof obj === "object" && "setInterval" in obj;
- },
-
- isNaN: function( obj ) {
- return obj == null || !rdigit.test( obj ) || isNaN( obj );
- },
-
- type: function( obj ) {
- return obj == null ?
- String( obj ) :
- class2type[ toString.call(obj) ] || "object";
- },
-
- isPlainObject: function( obj ) {
- // Must be an Object.
- // Because of IE, we also have to check the presence of the constructor property.
- // Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
- return false;
- }
-
- // Not own constructor property must be Object
- if ( obj.constructor &&
- !hasOwn.call(obj, "constructor") &&
- !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
- return false;
- }
-
- // Own properties are enumerated firstly, so to speed up,
- // if last one is own, then all properties are own.
-
- var key;
- for ( key in obj ) {}
-
- return key === undefined || hasOwn.call( obj, key );
- },
-
- isEmptyObject: function( obj ) {
- for ( var name in obj ) {
- return false;
- }
- return true;
- },
-
- error: function( msg ) {
- throw msg;
- },
-
- parseJSON: function( data ) {
- if ( typeof data !== "string" || !data ) {
- return null;
- }
-
- // Make sure leading/trailing whitespace is removed (IE can't handle it)
- data = jQuery.trim( data );
-
- // Attempt to parse using the native JSON parser first
- if ( window.JSON && window.JSON.parse ) {
- return window.JSON.parse( data );
- }
-
- // Make sure the incoming data is actual JSON
- // Logic borrowed from http://json.org/json2.js
- if ( rvalidchars.test( data.replace( rvalidescape, "@" )
- .replace( rvalidtokens, "]" )
- .replace( rvalidbraces, "")) ) {
-
- return (new Function( "return " + data ))();
-
- }
- jQuery.error( "Invalid JSON: " + data );
- },
-
- // Cross-browser xml parsing
- // (xml & tmp used internally)
- parseXML: function( data , xml , tmp ) {
-
- if ( window.DOMParser ) { // Standard
- tmp = new DOMParser();
- xml = tmp.parseFromString( data , "text/xml" );
- } else { // IE
- xml = new ActiveXObject( "Microsoft.XMLDOM" );
- xml.async = "false";
- xml.loadXML( data );
- }
-
- tmp = xml.documentElement;
-
- if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
- jQuery.error( "Invalid XML: " + data );
- }
-
- return xml;
- },
-
- noop: function() {},
-
- // Evaluates a script in a global context
- // Workarounds based on findings by Jim Driscoll
- // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
- globalEval: function( data ) {
- if ( data && rnotwhite.test( data ) ) {
- // We use execScript on Internet Explorer
- // We use an anonymous function so that context is window
- // rather than jQuery in Firefox
- ( window.execScript || function( data ) {
- window[ "eval" ].call( window, data );
- } )( data );
- }
- },
-
- // Converts a dashed string to camelCased string;
- // Used by both the css and data modules
- camelCase: function( string ) {
- return string.replace( rdashAlpha, fcamelCase );
- },
-
- nodeName: function( elem, name ) {
- return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
- },
-
- // args is for internal usage only
- each: function( object, callback, args ) {
- var name, i = 0,
- length = object.length,
- isObj = length === undefined || jQuery.isFunction( object );
-
- if ( args ) {
- if ( isObj ) {
- for ( name in object ) {
- if ( callback.apply( object[ name ], args ) === false ) {
- break;
- }
- }
- } else {
- for ( ; i < length; ) {
- if ( callback.apply( object[ i++ ], args ) === false ) {
- break;
- }
- }
- }
-
- // A special, fast, case for the most common use of each
- } else {
- if ( isObj ) {
- for ( name in object ) {
- if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
- break;
- }
- }
- } else {
- for ( ; i < length; ) {
- if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
- break;
- }
- }
- }
- }
-
- return object;
- },
-
- // Use native String.trim function wherever possible
- trim: trim ?
- function( text ) {
- return text == null ?
- "" :
- trim.call( text );
- } :
-
- // Otherwise use our own trimming functionality
- function( text ) {
- return text == null ?
- "" :
- text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
- },
-
- // results is for internal usage only
- makeArray: function( array, results ) {
- var ret = results || [];
-
- if ( array != null ) {
- // The window, strings (and functions) also have 'length'
- // The extra typeof function check is to prevent crashes
- // in Safari 2 (See: #3039)
- // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
- var type = jQuery.type( array );
-
- if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
- push.call( ret, array );
- } else {
- jQuery.merge( ret, array );
- }
- }
-
- return ret;
- },
-
- inArray: function( elem, array ) {
-
- if ( indexOf ) {
- return indexOf.call( array, elem );
- }
-
- for ( var i = 0, length = array.length; i < length; i++ ) {
- if ( array[ i ] === elem ) {
- return i;
- }
- }
-
- return -1;
- },
-
- merge: function( first, second ) {
- var i = first.length,
- j = 0;
-
- if ( typeof second.length === "number" ) {
- for ( var l = second.length; j < l; j++ ) {
- first[ i++ ] = second[ j ];
- }
-
- } else {
- while ( second[j] !== undefined ) {
- first[ i++ ] = second[ j++ ];
- }
- }
-
- first.length = i;
-
- return first;
- },
-
- grep: function( elems, callback, inv ) {
- var ret = [], retVal;
- inv = !!inv;
-
- // Go through the array, only saving the items
- // that pass the validator function
- for ( var i = 0, length = elems.length; i < length; i++ ) {
- retVal = !!callback( elems[ i ], i );
- if ( inv !== retVal ) {
- ret.push( elems[ i ] );
- }
- }
-
- return ret;
- },
-
- // arg is for internal usage only
- map: function( elems, callback, arg ) {
- var value, key, ret = [],
- i = 0,
- length = elems.length,
- // jquery objects are treated as arrays
- isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
-
- // Go through the array, translating each of the items to their
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
-
- // Go through every key on the object,
- } else {
- for ( key in elems ) {
- value = callback( elems[ key ], key, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
- }
-
- // Flatten any nested arrays
- return ret.concat.apply( [], ret );
- },
-
- // A global GUID counter for objects
- guid: 1,
-
- // Bind a function to a context, optionally partially applying any
- // arguments.
- proxy: function( fn, context ) {
- if ( typeof context === "string" ) {
- var tmp = fn[ context ];
- context = fn;
- fn = tmp;
- }
-
- // Quick check to determine if target is callable, in the spec
- // this throws a TypeError, but we will just return undefined.
- if ( !jQuery.isFunction( fn ) ) {
- return undefined;
- }
-
- // Simulated bind
- var args = slice.call( arguments, 2 ),
- proxy = function() {
- return fn.apply( context, args.concat( slice.call( arguments ) ) );
- };
-
- // Set the guid of unique handler to the same of original handler, so it can be removed
- proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
-
- return proxy;
- },
-
- // Mutifunctional method to get and set values to a collection
- // The value/s can optionally be executed if it's a function
- access: function( elems, key, value, exec, fn, pass ) {
- var length = elems.length;
-
- // Setting many attributes
- if ( typeof key === "object" ) {
- for ( var k in key ) {
- jQuery.access( elems, k, key[k], exec, fn, value );
- }
- return elems;
- }
-
- // Setting one attribute
- if ( value !== undefined ) {
- // Optionally, function values get executed if exec is true
- exec = !pass && exec && jQuery.isFunction(value);
-
- for ( var i = 0; i < length; i++ ) {
- fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
- }
-
- return elems;
- }
-
- // Getting an attribute
- return length ? fn( elems[0], key ) : undefined;
- },
-
- now: function() {
- return (new Date()).getTime();
- },
-
- // Use of jQuery.browser is frowned upon.
- // More details: http://docs.jquery.com/Utilities/jQuery.browser
- uaMatch: function( ua ) {
- ua = ua.toLowerCase();
-
- var match = rwebkit.exec( ua ) ||
- ropera.exec( ua ) ||
- rmsie.exec( ua ) ||
- ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
- [];
-
- return { browser: match[1] || "", version: match[2] || "0" };
- },
-
- sub: function() {
- function jQuerySub( selector, context ) {
- return new jQuerySub.fn.init( selector, context );
- }
- jQuery.extend( true, jQuerySub, this );
- jQuerySub.superclass = this;
- jQuerySub.fn = jQuerySub.prototype = this();
- jQuerySub.fn.constructor = jQuerySub;
- jQuerySub.sub = this.sub;
- jQuerySub.fn.init = function init( selector, context ) {
- if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
- context = jQuerySub( context );
- }
-
- return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
- };
- jQuerySub.fn.init.prototype = jQuerySub.fn;
- var rootjQuerySub = jQuerySub(document);
- return jQuerySub;
- },
-
- browser: {}
-});
-
-// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
-});
-
-browserMatch = jQuery.uaMatch( userAgent );
-if ( browserMatch.browser ) {
- jQuery.browser[ browserMatch.browser ] = true;
- jQuery.browser.version = browserMatch.version;
-}
-
-// Deprecated, use jQuery.browser.webkit instead
-if ( jQuery.browser.webkit ) {
- jQuery.browser.safari = true;
-}
-
-// IE doesn't match non-breaking spaces with \s
-if ( rnotwhite.test( "\xA0" ) ) {
- trimLeft = /^[\s\xA0]+/;
- trimRight = /[\s\xA0]+$/;
-}
-
-// All jQuery objects should point back to these
-rootjQuery = jQuery(document);
-
-// Cleanup functions for the document ready method
-if ( document.addEventListener ) {
- DOMContentLoaded = function() {
- document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
- jQuery.ready();
- };
-
-} else if ( document.attachEvent ) {
- DOMContentLoaded = function() {
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( document.readyState === "complete" ) {
- document.detachEvent( "onreadystatechange", DOMContentLoaded );
- jQuery.ready();
- }
- };
-}
-
-// The DOM ready check for Internet Explorer
-function doScrollCheck() {
- if ( jQuery.isReady ) {
- return;
- }
-
- try {
- // If IE is used, use the trick by Diego Perini
- // http://javascript.nwbox.com/IEContentLoaded/
- document.documentElement.doScroll("left");
- } catch(e) {
- setTimeout( doScrollCheck, 1 );
- return;
- }
-
- // and execute any waiting functions
- jQuery.ready();
-}
-
-return jQuery;
-
-})();
-
-
-var // Promise methods
- promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
- // Static reference to slice
- sliceDeferred = [].slice;
-
-jQuery.extend({
- // Create a simple deferred (one callbacks list)
- _Deferred: function() {
- var // callbacks list
- callbacks = [],
- // stored [ context , args ]
- fired,
- // to avoid firing when already doing so
- firing,
- // flag to know if the deferred has been cancelled
- cancelled,
- // the deferred itself
- deferred = {
-
- // done( f1, f2, ...)
- done: function() {
- if ( !cancelled ) {
- var args = arguments,
- i,
- length,
- elem,
- type,
- _fired;
- if ( fired ) {
- _fired = fired;
- fired = 0;
- }
- for ( i = 0, length = args.length; i < length; i++ ) {
- elem = args[ i ];
- type = jQuery.type( elem );
- if ( type === "array" ) {
- deferred.done.apply( deferred, elem );
- } else if ( type === "function" ) {
- callbacks.push( elem );
- }
- }
- if ( _fired ) {
- deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
- }
- }
- return this;
- },
-
- // resolve with given context and args
- resolveWith: function( context, args ) {
- if ( !cancelled && !fired && !firing ) {
- // make sure args are available (#8421)
- args = args || [];
- firing = 1;
- try {
- while( callbacks[ 0 ] ) {
- callbacks.shift().apply( context, args );
- }
- }
- finally {
- fired = [ context, args ];
- firing = 0;
- }
- }
- return this;
- },
-
- // resolve with this as context and given arguments
- resolve: function() {
- deferred.resolveWith( this, arguments );
- return this;
- },
-
- // Has this deferred been resolved?
- isResolved: function() {
- return !!( firing || fired );
- },
-
- // Cancel
- cancel: function() {
- cancelled = 1;
- callbacks = [];
- return this;
- }
- };
-
- return deferred;
- },
-
- // Full fledged deferred (two callbacks list)
- Deferred: function( func ) {
- var deferred = jQuery._Deferred(),
- failDeferred = jQuery._Deferred(),
- promise;
- // Add errorDeferred methods, then and promise
- jQuery.extend( deferred, {
- then: function( doneCallbacks, failCallbacks ) {
- deferred.done( doneCallbacks ).fail( failCallbacks );
- return this;
- },
- always: function() {
- return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
- },
- fail: failDeferred.done,
- rejectWith: failDeferred.resolveWith,
- reject: failDeferred.resolve,
- isRejected: failDeferred.isResolved,
- pipe: function( fnDone, fnFail ) {
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( {
- done: [ fnDone, "resolve" ],
- fail: [ fnFail, "reject" ]
- }, function( handler, data ) {
- var fn = data[ 0 ],
- action = data[ 1 ],
- returned;
- if ( jQuery.isFunction( fn ) ) {
- deferred[ handler ](function() {
- returned = fn.apply( this, arguments );
- if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise().then( newDefer.resolve, newDefer.reject );
- } else {
- newDefer[ action ]( returned );
- }
- });
- } else {
- deferred[ handler ]( newDefer[ action ] );
- }
- });
- }).promise();
- },
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- if ( obj == null ) {
- if ( promise ) {
- return promise;
- }
- promise = obj = {};
- }
- var i = promiseMethods.length;
- while( i-- ) {
- obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
- }
- return obj;
- }
- });
- // Make sure only one callback list will be used
- deferred.done( failDeferred.cancel ).fail( deferred.cancel );
- // Unexpose cancel
- delete deferred.cancel;
- // Call given func if any
- if ( func ) {
- func.call( deferred, deferred );
- }
- return deferred;
- },
-
- // Deferred helper
- when: function( firstParam ) {
- var args = arguments,
- i = 0,
- length = args.length,
- count = length,
- deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
- firstParam :
- jQuery.Deferred();
- function resolveFunc( i ) {
- return function( value ) {
- args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
- if ( !( --count ) ) {
- // Strange bug in FF4:
- // Values changed onto the arguments object sometimes end up as undefined values
- // outside the $.when method. Cloning the object into a fresh array solves the issue
- deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
- }
- };
- }
- if ( length > 1 ) {
- for( ; i < length; i++ ) {
- if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
- args[ i ].promise().then( resolveFunc(i), deferred.reject );
- } else {
- --count;
- }
- }
- if ( !count ) {
- deferred.resolveWith( deferred, args );
- }
- } else if ( deferred !== firstParam ) {
- deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
- }
- return deferred.promise();
- }
-});
-
-
-
-jQuery.support = (function() {
-
- var div = document.createElement( "div" ),
- documentElement = document.documentElement,
- all,
- a,
- select,
- opt,
- input,
- marginDiv,
- support,
- fragment,
- body,
- testElementParent,
- testElement,
- testElementStyle,
- tds,
- events,
- eventName,
- i,
- isSupported;
-
- // Preliminary tests
- div.setAttribute("className", "t");
- div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
-
- all = div.getElementsByTagName( "*" );
- a = div.getElementsByTagName( "a" )[ 0 ];
-
- // Can't get basic test support
- if ( !all || !all.length || !a ) {
- return {};
- }
-
- // First batch of supports tests
- select = document.createElement( "select" );
- opt = select.appendChild( document.createElement("option") );
- input = div.getElementsByTagName( "input" )[ 0 ];
-
- support = {
- // IE strips leading whitespace when .innerHTML is used
- leadingWhitespace: ( div.firstChild.nodeType === 3 ),
-
- // Make sure that tbody elements aren't automatically inserted
- // IE will insert them into empty tables
- tbody: !div.getElementsByTagName( "tbody" ).length,
-
- // Make sure that link elements get serialized correctly by innerHTML
- // This requires a wrapper element in IE
- htmlSerialize: !!div.getElementsByTagName( "link" ).length,
-
- // Get the style information from getAttribute
- // (IE uses .cssText instead)
- style: /top/.test( a.getAttribute("style") ),
-
- // Make sure that URLs aren't manipulated
- // (IE normalizes it by default)
- hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
-
- // Make sure that element opacity exists
- // (IE uses filter instead)
- // Use a regex to work around a WebKit issue. See #5145
- opacity: /^0.55$/.test( a.style.opacity ),
-
- // Verify style float existence
- // (IE uses styleFloat instead of cssFloat)
- cssFloat: !!a.style.cssFloat,
-
- // Make sure that if no value is specified for a checkbox
- // that it defaults to "on".
- // (WebKit defaults to "" instead)
- checkOn: ( input.value === "on" ),
-
- // Make sure that a selected-by-default option has a working selected property.
- // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
- optSelected: opt.selected,
-
- // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
- getSetAttribute: div.className !== "t",
-
- // Will be defined later
- submitBubbles: true,
- changeBubbles: true,
- focusinBubbles: false,
- deleteExpando: true,
- noCloneEvent: true,
- inlineBlockNeedsLayout: false,
- shrinkWrapBlocks: false,
- reliableMarginRight: true
- };
-
- // Make sure checked status is properly cloned
- input.checked = true;
- support.noCloneChecked = input.cloneNode( true ).checked;
-
- // Make sure that the options inside disabled selects aren't marked as disabled
- // (WebKit marks them as disabled)
- select.disabled = true;
- support.optDisabled = !opt.disabled;
-
- // Test to see if it's possible to delete an expando from an element
- // Fails in Internet Explorer
- try {
- delete div.test;
- } catch( e ) {
- support.deleteExpando = false;
- }
-
- if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
- div.attachEvent( "onclick", function() {
- // Cloning a node shouldn't copy over any
- // bound event handlers (IE does this)
- support.noCloneEvent = false;
- });
- div.cloneNode( true ).fireEvent( "onclick" );
- }
-
- // Check if a radio maintains it's value
- // after being appended to the DOM
- input = document.createElement("input");
- input.value = "t";
- input.setAttribute("type", "radio");
- support.radioValue = input.value === "t";
-
- input.setAttribute("checked", "checked");
- div.appendChild( input );
- fragment = document.createDocumentFragment();
- fragment.appendChild( div.firstChild );
-
- // WebKit doesn't clone checked state correctly in fragments
- support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
- div.innerHTML = "";
-
- // Figure out if the W3C box model works as expected
- div.style.width = div.style.paddingLeft = "1px";
-
- body = document.getElementsByTagName( "body" )[ 0 ];
- // We use our own, invisible, body unless the body is already present
- // in which case we use a div (#9239)
- testElement = document.createElement( body ? "div" : "body" );
- testElementStyle = {
- visibility: "hidden",
- width: 0,
- height: 0,
- border: 0,
- margin: 0
- };
- if ( body ) {
- jQuery.extend( testElementStyle, {
- position: "absolute",
- left: -1000,
- top: -1000
- });
- }
- for ( i in testElementStyle ) {
- testElement.style[ i ] = testElementStyle[ i ];
- }
- testElement.appendChild( div );
- testElementParent = body || documentElement;
- testElementParent.insertBefore( testElement, testElementParent.firstChild );
-
- // Check if a disconnected checkbox will retain its checked
- // value of true after appended to the DOM (IE6/7)
- support.appendChecked = input.checked;
-
- support.boxModel = div.offsetWidth === 2;
-
- if ( "zoom" in div.style ) {
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- // (IE < 8 does this)
- div.style.display = "inline";
- div.style.zoom = 1;
- support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
-
- // Check if elements with layout shrink-wrap their children
- // (IE 6 does this)
- div.style.display = "";
- div.innerHTML = "<div style='width:4px;'></div>";
- support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
- }
-
- div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
- tds = div.getElementsByTagName( "td" );
-
- // Check if table cells still have offsetWidth/Height when they are set
- // to display:none and there are still other visible table cells in a
- // table row; if so, offsetWidth/Height are not reliable for use when
- // determining if an element has been hidden directly using
- // display:none (it is still safe to use offsets if a parent element is
- // hidden; don safety goggles and see bug #4512 for more information).
- // (only IE 8 fails this test)
- isSupported = ( tds[ 0 ].offsetHeight === 0 );
-
- tds[ 0 ].style.display = "";
- tds[ 1 ].style.display = "none";
-
- // Check if empty table cells still have offsetWidth/Height
- // (IE < 8 fail this test)
- support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
- div.innerHTML = "";
-
- // Check if div with explicit width and no margin-right incorrectly
- // gets computed margin-right based on width of container. For more
- // info see bug #3333
- // Fails in WebKit before Feb 2011 nightlies
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- if ( document.defaultView && document.defaultView.getComputedStyle ) {
- marginDiv = document.createElement( "div" );
- marginDiv.style.width = "0";
- marginDiv.style.marginRight = "0";
- div.appendChild( marginDiv );
- support.reliableMarginRight =
- ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
- }
-
- // Remove the body element we added
- testElement.innerHTML = "";
- testElementParent.removeChild( testElement );
-
- // Technique from Juriy Zaytsev
- // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
- // We only care about the case where non-standard event systems
- // are used, namely in IE. Short-circuiting here helps us to
- // avoid an eval call (in setAttribute) which can cause CSP
- // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
- if ( div.attachEvent ) {
- for( i in {
- submit: 1,
- change: 1,
- focusin: 1
- } ) {
- eventName = "on" + i;
- isSupported = ( eventName in div );
- if ( !isSupported ) {
- div.setAttribute( eventName, "return;" );
- isSupported = ( typeof div[ eventName ] === "function" );
- }
- support[ i + "Bubbles" ] = isSupported;
- }
- }
-
- // Null connected elements to avoid leaks in IE
- testElement = fragment = select = opt = body = marginDiv = div = input = null;
-
- return support;
-})();
-
-// Keep track of boxModel
-jQuery.boxModel = jQuery.support.boxModel;
-
-
-
-
-var rbrace = /^(?:\{.*\}|\[.*\])$/,
- rmultiDash = /([a-z])([A-Z])/g;
-
-jQuery.extend({
- cache: {},
-
- // Please use with caution
- uuid: 0,
-
- // Unique for each copy of jQuery on the page
- // Non-digits removed to match rinlinejQuery
- expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
-
- // The following elements throw uncatchable exceptions if you
- // attempt to add expando properties to them.
- noData: {
- "embed": true,
- // Ban all objects except for Flash (which handle expandos)
- "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
- "applet": true
- },
-
- hasData: function( elem ) {
- elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
-
- return !!elem && !isEmptyDataObject( elem );
- },
-
- data: function( elem, name, data, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
-
- // We have to handle DOM nodes and JS objects differently because IE6-7
- // can't GC object references properly across the DOM-JS boundary
- isNode = elem.nodeType,
-
- // Only DOM nodes need the global jQuery cache; JS object data is
- // attached directly to the object so GC can occur automatically
- cache = isNode ? jQuery.cache : elem,
-
- // Only defining an ID for JS objects if its cache already exists allows
- // the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
-
- // Avoid doing any more work than we need to when trying to get data on an
- // object that has no data at all
- if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
- return;
- }
-
- if ( !id ) {
- // Only DOM nodes need a new unique ID for each element since their data
- // ends up in the global cache
- if ( isNode ) {
- elem[ jQuery.expando ] = id = ++jQuery.uuid;
- } else {
- id = jQuery.expando;
- }
- }
-
- if ( !cache[ id ] ) {
- cache[ id ] = {};
-
- // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
- // metadata on plain JS objects when the object is serialized using
- // JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
- }
-
- // An object can be passed to jQuery.data instead of a key/value pair; this gets
- // shallow copied over onto the existing cache
- if ( typeof name === "object" || typeof name === "function" ) {
- if ( pvt ) {
- cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
- } else {
- cache[ id ] = jQuery.extend(cache[ id ], name);
- }
- }
-
- thisCache = cache[ id ];
-
- // Internal jQuery data is stored in a separate object inside the object's data
- // cache in order to avoid key collisions between internal data and user-defined
- // data
- if ( pvt ) {
- if ( !thisCache[ internalKey ] ) {
- thisCache[ internalKey ] = {};
- }
-
- thisCache = thisCache[ internalKey ];
- }
-
- if ( data !== undefined ) {
- thisCache[ jQuery.camelCase( name ) ] = data;
- }
-
- // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
- // not attempt to inspect the internal events object using jQuery.data, as this
- // internal data object is undocumented and subject to change.
- if ( name === "events" && !thisCache[name] ) {
- return thisCache[ internalKey ] && thisCache[ internalKey ].events;
- }
-
- return getByName ?
- // Check for both converted-to-camel and non-converted data property names
- thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] :
- thisCache;
- },
-
- removeData: function( elem, name, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var internalKey = jQuery.expando, isNode = elem.nodeType,
-
- // See jQuery.data for more information
- cache = isNode ? jQuery.cache : elem,
-
- // See jQuery.data for more information
- id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
-
- // If there is already no cache entry for this object, there is no
- // purpose in continuing
- if ( !cache[ id ] ) {
- return;
- }
-
- if ( name ) {
- var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
-
- if ( thisCache ) {
- delete thisCache[ name ];
-
- // If there is no data left in the cache, we want to continue
- // and let the cache object itself get destroyed
- if ( !isEmptyDataObject(thisCache) ) {
- return;
- }
- }
- }
-
- // See jQuery.data for more information
- if ( pvt ) {
- delete cache[ id ][ internalKey ];
-
- // Don't destroy the parent cache unless the internal data object
- // had been the only thing left in it
- if ( !isEmptyDataObject(cache[ id ]) ) {
- return;
- }
- }
-
- var internalCache = cache[ id ][ internalKey ];
-
- // Browsers that fail expando deletion also refuse to delete expandos on
- // the window, but it will allow it on all other JS objects; other browsers
- // don't care
- if ( jQuery.support.deleteExpando || cache != window ) {
- delete cache[ id ];
- } else {
- cache[ id ] = null;
- }
-
- // We destroyed the entire user cache at once because it's faster than
- // iterating through each key, but we need to continue to persist internal
- // data if it existed
- if ( internalCache ) {
- cache[ id ] = {};
- // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
- // metadata on plain JS objects when the object is serialized using
- // JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
-
- cache[ id ][ internalKey ] = internalCache;
-
- // Otherwise, we need to eliminate the expando on the node to avoid
- // false lookups in the cache for entries that no longer exist
- } else if ( isNode ) {
- // IE does not allow us to delete expando properties from nodes,
- // nor does it have a removeAttribute function on Document nodes;
- // we must handle all of these cases
- if ( jQuery.support.deleteExpando ) {
- delete elem[ jQuery.expando ];
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
- } else {
- elem[ jQuery.expando ] = null;
- }
- }
- },
-
- // For internal use only.
- _data: function( elem, name, data ) {
- return jQuery.data( elem, name, data, true );
- },
-
- // A method for determining if a DOM node can handle the data expando
- acceptData: function( elem ) {
- if ( elem.nodeName ) {
- var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
-
- if ( match ) {
- return !(match === true || elem.getAttribute("classid") !== match);
- }
- }
-
- return true;
- }
-});
-
-jQuery.fn.extend({
- data: function( key, value ) {
- var data = null;
-
- if ( typeof key === "undefined" ) {
- if ( this.length ) {
- data = jQuery.data( this[0] );
-
- if ( this[0].nodeType === 1 ) {
- var attr = this[0].attributes, name;
- for ( var i = 0, l = attr.length; i < l; i++ ) {
- name = attr[i].name;
-
- if ( name.indexOf( "data-" ) === 0 ) {
- name = jQuery.camelCase( name.substring(5) );
-
- dataAttr( this[0], name, data[ name ] );
- }
- }
- }
- }
-
- return data;
-
- } else if ( typeof key === "object" ) {
- return this.each(function() {
- jQuery.data( this, key );
- });
- }
-
- var parts = key.split(".");
- parts[1] = parts[1] ? "." + parts[1] : "";
-
- if ( value === undefined ) {
- data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-
- // Try to fetch any internally stored data first
- if ( data === undefined && this.length ) {
- data = jQuery.data( this[0], key );
- data = dataAttr( this[0], key, data );
- }
-
- return data === undefined && parts[1] ?
- this.data( parts[0] ) :
- data;
-
- } else {
- return this.each(function() {
- var $this = jQuery( this ),
- args = [ parts[0], value ];
-
- $this.triggerHandler( "setData" + parts[1] + "!", args );
- jQuery.data( this, key, value );
- $this.triggerHandler( "changeData" + parts[1] + "!", args );
- });
- }
- },
-
- removeData: function( key ) {
- return this.each(function() {
- jQuery.removeData( this, key );
- });
- }
-});
-
-function dataAttr( elem, key, data ) {
- // If nothing was found internally, try to fetch any
- // data from the HTML5 data-* attribute
- if ( data === undefined && elem.nodeType === 1 ) {
- var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase();
-
- data = elem.getAttribute( name );
-
- if ( typeof data === "string" ) {
- try {
- data = data === "true" ? true :
- data === "false" ? false :
- data === "null" ? null :
- !jQuery.isNaN( data ) ? parseFloat( data ) :
- rbrace.test( data ) ? jQuery.parseJSON( data ) :
- data;
- } catch( e ) {}
-
- // Make sure we set the data so it isn't changed later
- jQuery.data( elem, key, data );
-
- } else {
- data = undefined;
- }
- }
-
- return data;
-}
-
-// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
-// property to be considered empty objects; this property always exists in
-// order to make sure JSON.stringify does not expose internal metadata
-function isEmptyDataObject( obj ) {
- for ( var name in obj ) {
- if ( name !== "toJSON" ) {
- return false;
- }
- }
-
- return true;
-}
-
-
-
-
-function handleQueueMarkDefer( elem, type, src ) {
- var deferDataKey = type + "defer",
- queueDataKey = type + "queue",
- markDataKey = type + "mark",
- defer = jQuery.data( elem, deferDataKey, undefined, true );
- if ( defer &&
- ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
- ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) {
- // Give room for hard-coded callbacks to fire first
- // and eventually mark/queue something else on the element
- setTimeout( function() {
- if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
- !jQuery.data( elem, markDataKey, undefined, true ) ) {
- jQuery.removeData( elem, deferDataKey, true );
- defer.resolve();
- }
- }, 0 );
- }
-}
-
-jQuery.extend({
-
- _mark: function( elem, type ) {
- if ( elem ) {
- type = (type || "fx") + "mark";
- jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true );
- }
- },
-
- _unmark: function( force, elem, type ) {
- if ( force !== true ) {
- type = elem;
- elem = force;
- force = false;
- }
- if ( elem ) {
- type = type || "fx";
- var key = type + "mark",
- count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 );
- if ( count ) {
- jQuery.data( elem, key, count, true );
- } else {
- jQuery.removeData( elem, key, true );
- handleQueueMarkDefer( elem, type, "mark" );
- }
- }
- },
-
- queue: function( elem, type, data ) {
- if ( elem ) {
- type = (type || "fx") + "queue";
- var q = jQuery.data( elem, type, undefined, true );
- // Speed up dequeue by getting out quickly if this is just a lookup
- if ( data ) {
- if ( !q || jQuery.isArray(data) ) {
- q = jQuery.data( elem, type, jQuery.makeArray(data), true );
- } else {
- q.push( data );
- }
- }
- return q || [];
- }
- },
-
- dequeue: function( elem, type ) {
- type = type || "fx";
-
- var queue = jQuery.queue( elem, type ),
- fn = queue.shift(),
- defer;
-
- // If the fx queue is dequeued, always remove the progress sentinel
- if ( fn === "inprogress" ) {
- fn = queue.shift();
- }
-
- if ( fn ) {
- // Add a progress sentinel to prevent the fx queue from being
- // automatically dequeued
- if ( type === "fx" ) {
- queue.unshift("inprogress");
- }
-
- fn.call(elem, function() {
- jQuery.dequeue(elem, type);
- });
- }
-
- if ( !queue.length ) {
- jQuery.removeData( elem, type + "queue", true );
- handleQueueMarkDefer( elem, type, "queue" );
- }
- }
-});
-
-jQuery.fn.extend({
- queue: function( type, data ) {
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- }
-
- if ( data === undefined ) {
- return jQuery.queue( this[0], type );
- }
- return this.each(function() {
- var queue = jQuery.queue( this, type, data );
-
- if ( type === "fx" && queue[0] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- });
- },
- dequeue: function( type ) {
- return this.each(function() {
- jQuery.dequeue( this, type );
- });
- },
- // Based off of the plugin by Clint Helfers, with permission.
- // http://blindsignals.com/index.php/2009/07/jquery-delay/
- delay: function( time, type ) {
- time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
- type = type || "fx";
-
- return this.queue( type, function() {
- var elem = this;
- setTimeout(function() {
- jQuery.dequeue( elem, type );
- }, time );
- });
- },
- clearQueue: function( type ) {
- return this.queue( type || "fx", [] );
- },
- // Get a promise resolved when queues of a certain type
- // are emptied (fx is the type by default)
- promise: function( type, object ) {
- if ( typeof type !== "string" ) {
- object = type;
- type = undefined;
- }
- type = type || "fx";
- var defer = jQuery.Deferred(),
- elements = this,
- i = elements.length,
- count = 1,
- deferDataKey = type + "defer",
- queueDataKey = type + "queue",
- markDataKey = type + "mark",
- tmp;
- function resolve() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- }
- while( i-- ) {
- if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
- ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
- jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
- jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
- count++;
- tmp.done( resolve );
- }
- }
- resolve();
- return defer.promise();
- }
-});
-
-
-
-
-var rclass = /[\n\t\r]/g,
- rspace = /\s+/,
- rreturn = /\r/g,
- rtype = /^(?:button|input)$/i,
- rfocusable = /^(?:button|input|object|select|textarea)$/i,
- rclickable = /^a(?:rea)?$/i,
- rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- rinvalidChar = /\:|^on/,
- formHook, boolHook;
-
-jQuery.fn.extend({
- attr: function( name, value ) {
- return jQuery.access( this, name, value, true, jQuery.attr );
- },
-
- removeAttr: function( name ) {
- return this.each(function() {
- jQuery.removeAttr( this, name );
- });
- },
-
- prop: function( name, value ) {
- return jQuery.access( this, name, value, true, jQuery.prop );
- },
-
- removeProp: function( name ) {
- name = jQuery.propFix[ name ] || name;
- return this.each(function() {
- // try/catch handles cases where IE balks (such as removing a property on window)
- try {
- this[ name ] = undefined;
- delete this[ name ];
- } catch( e ) {}
- });
- },
-
- addClass: function( value ) {
- var classNames, i, l, elem,
- setClass, c, cl;
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).addClass( value.call(this, j, this.className) );
- });
- }
-
- if ( value && typeof value === "string" ) {
- classNames = value.split( rspace );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- elem = this[ i ];
-
- if ( elem.nodeType === 1 ) {
- if ( !elem.className && classNames.length === 1 ) {
- elem.className = value;
-
- } else {
- setClass = " " + elem.className + " ";
-
- for ( c = 0, cl = classNames.length; c < cl; c++ ) {
- if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
- setClass += classNames[ c ] + " ";
- }
- }
- elem.className = jQuery.trim( setClass );
- }
- }
- }
- }
-
- return this;
- },
-
- removeClass: function( value ) {
- var classNames, i, l, elem, className, c, cl;
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).removeClass( value.call(this, j, this.className) );
- });
- }
-
- if ( (value && typeof value === "string") || value === undefined ) {
- classNames = (value || "").split( rspace );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- elem = this[ i ];
-
- if ( elem.nodeType === 1 && elem.className ) {
- if ( value ) {
- className = (" " + elem.className + " ").replace( rclass, " " );
- for ( c = 0, cl = classNames.length; c < cl; c++ ) {
- className = className.replace(" " + classNames[ c ] + " ", " ");
- }
- elem.className = jQuery.trim( className );
-
- } else {
- elem.className = "";
- }
- }
- }
- }
-
- return this;
- },
-
- toggleClass: function( value, stateVal ) {
- var type = typeof value,
- isBool = typeof stateVal === "boolean";
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( i ) {
- jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
- });
- }
-
- return this.each(function() {
- if ( type === "string" ) {
- // toggle individual class names
- var className,
- i = 0,
- self = jQuery( this ),
- state = stateVal,
- classNames = value.split( rspace );
-
- while ( (className = classNames[ i++ ]) ) {
- // check each className given, space seperated list
- state = isBool ? state : !self.hasClass( className );
- self[ state ? "addClass" : "removeClass" ]( className );
- }
-
- } else if ( type === "undefined" || type === "boolean" ) {
- if ( this.className ) {
- // store className if set
- jQuery._data( this, "__className__", this.className );
- }
-
- // toggle whole className
- this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
- }
- });
- },
-
- hasClass: function( selector ) {
- var className = " " + selector + " ";
- for ( var i = 0, l = this.length; i < l; i++ ) {
- if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
- return true;
- }
- }
-
- return false;
- },
-
- val: function( value ) {
- var hooks, ret,
- elem = this[0];
-
- if ( !arguments.length ) {
- if ( elem ) {
- hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
-
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
- return ret;
- }
-
- ret = elem.value;
-
- return typeof ret === "string" ?
- // handle most common string cases
- ret.replace(rreturn, "") :
- // handle cases where value is null/undef or number
- ret == null ? "" : ret;
- }
-
- return undefined;
- }
-
- var isFunction = jQuery.isFunction( value );
-
- return this.each(function( i ) {
- var self = jQuery(this), val;
-
- if ( this.nodeType !== 1 ) {
- return;
- }
-
- if ( isFunction ) {
- val = value.call( this, i, self.val() );
- } else {
- val = value;
- }
-
- // Treat null/undefined as ""; convert numbers to string
- if ( val == null ) {
- val = "";
- } else if ( typeof val === "number" ) {
- val += "";
- } else if ( jQuery.isArray( val ) ) {
- val = jQuery.map(val, function ( value ) {
- return value == null ? "" : value + "";
- });
- }
-
- hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
-
- // If set returns undefined, fall back to normal setting
- if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
- this.value = val;
- }
- });
- }
-});
-
-jQuery.extend({
- valHooks: {
- option: {
- get: function( elem ) {
- // attributes.value is undefined in Blackberry 4.7 but
- // uses .value. See #6932
- var val = elem.attributes.value;
- return !val || val.specified ? elem.value : elem.text;
- }
- },
- select: {
- get: function( elem ) {
- var value,
- index = elem.selectedIndex,
- values = [],
- options = elem.options,
- one = elem.type === "select-one";
-
- // Nothing was selected
- if ( index < 0 ) {
- return null;
- }
-
- // Loop through all the selected options
- for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
- var option = options[ i ];
-
- // Don't return options that are disabled or in a disabled optgroup
- if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
- (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
-
- // Get the specific value for the option
- value = jQuery( option ).val();
-
- // We don't need an array for one selects
- if ( one ) {
- return value;
- }
-
- // Multi-Selects return an array
- values.push( value );
- }
- }
-
- // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
- if ( one && !values.length && options.length ) {
- return jQuery( options[ index ] ).val();
- }
-
- return values;
- },
-
- set: function( elem, value ) {
- var values = jQuery.makeArray( value );
-
- jQuery(elem).find("option").each(function() {
- this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
- });
-
- if ( !values.length ) {
- elem.selectedIndex = -1;
- }
- return values;
- }
- }
- },
-
- attrFn: {
- val: true,
- css: true,
- html: true,
- text: true,
- data: true,
- width: true,
- height: true,
- offset: true
- },
-
- attrFix: {
- // Always normalize to ensure hook usage
- tabindex: "tabIndex"
- },
-
- attr: function( elem, name, value, pass ) {
- var nType = elem.nodeType;
-
- // don't get/set attributes on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return undefined;
- }
-
- if ( pass && name in jQuery.attrFn ) {
- return jQuery( elem )[ name ]( value );
- }
-
- // Fallback to prop when attributes are not supported
- if ( !("getAttribute" in elem) ) {
- return jQuery.prop( elem, name, value );
- }
-
- var ret, hooks,
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- // Normalize the name if needed
- if ( notxml ) {
- name = jQuery.attrFix[ name ] || name;
-
- hooks = jQuery.attrHooks[ name ];
-
- if ( !hooks ) {
- // Use boolHook for boolean attributes
- if ( rboolean.test( name ) ) {
-
- hooks = boolHook;
-
- // Use formHook for forms and if the name contains certain characters
- } else if ( formHook && name !== "className" &&
- (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) {
-
- hooks = formHook;
- }
- }
- }
-
- if ( value !== undefined ) {
-
- if ( value === null ) {
- jQuery.removeAttr( elem, name );
- return undefined;
-
- } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- elem.setAttribute( name, "" + value );
- return value;
- }
-
- } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
-
- } else {
-
- ret = elem.getAttribute( name );
-
- // Non-existent attributes return null, we normalize to undefined
- return ret === null ?
- undefined :
- ret;
- }
- },
-
- removeAttr: function( elem, name ) {
- var propName;
- if ( elem.nodeType === 1 ) {
- name = jQuery.attrFix[ name ] || name;
-
- if ( jQuery.support.getSetAttribute ) {
- // Use removeAttribute in browsers that support it
- elem.removeAttribute( name );
- } else {
- jQuery.attr( elem, name, "" );
- elem.removeAttributeNode( elem.getAttributeNode( name ) );
- }
-
- // Set corresponding property to false for boolean attributes
- if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) {
- elem[ propName ] = false;
- }
- }
- },
-
- attrHooks: {
- type: {
- set: function( elem, value ) {
- // We can't allow the type property to be changed (since it causes problems in IE)
- if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
- jQuery.error( "type property can't be changed" );
- } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
- // Setting the type on a radio button after the value resets the value in IE6-9
- // Reset value to it's default in case type is set after value
- // This is for element creation
- var val = elem.value;
- elem.setAttribute( "type", value );
- if ( val ) {
- elem.value = val;
- }
- return value;
- }
- }
- },
- tabIndex: {
- get: function( elem ) {
- // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
- // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
- var attributeNode = elem.getAttributeNode("tabIndex");
-
- return attributeNode && attributeNode.specified ?
- parseInt( attributeNode.value, 10 ) :
- rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
- 0 :
- undefined;
- }
- },
- // Use the value property for back compat
- // Use the formHook for button elements in IE6/7 (#1954)
- value: {
- get: function( elem, name ) {
- if ( formHook && jQuery.nodeName( elem, "button" ) ) {
- return formHook.get( elem, name );
- }
- return name in elem ?
- elem.value :
- null;
- },
- set: function( elem, value, name ) {
- if ( formHook && jQuery.nodeName( elem, "button" ) ) {
- return formHook.set( elem, value, name );
- }
- // Does not return so that setAttribute is also used
- elem.value = value;
- }
- }
- },
-
- propFix: {
- tabindex: "tabIndex",
- readonly: "readOnly",
- "for": "htmlFor",
- "class": "className",
- maxlength: "maxLength",
- cellspacing: "cellSpacing",
- cellpadding: "cellPadding",
- rowspan: "rowSpan",
- colspan: "colSpan",
- usemap: "useMap",
- frameborder: "frameBorder",
- contenteditable: "contentEditable"
- },
-
- prop: function( elem, name, value ) {
- var nType = elem.nodeType;
-
- // don't get/set properties on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return undefined;
- }
-
- var ret, hooks,
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- if ( notxml ) {
- // Fix name and attach hooks
- name = jQuery.propFix[ name ] || name;
- hooks = jQuery.propHooks[ name ];
- }
-
- if ( value !== undefined ) {
- if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- return (elem[ name ] = value);
- }
-
- } else {
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) {
- return ret;
-
- } else {
- return elem[ name ];
- }
- }
- },
-
- propHooks: {}
-});
-
-// Hook for boolean attributes
-boolHook = {
- get: function( elem, name ) {
- // Align boolean attributes with corresponding properties
- return jQuery.prop( elem, name ) ?
- name.toLowerCase() :
- undefined;
- },
- set: function( elem, value, name ) {
- var propName;
- if ( value === false ) {
- // Remove boolean attributes when set to false
- jQuery.removeAttr( elem, name );
- } else {
- // value is true since we know at this point it's type boolean and not false
- // Set boolean attributes to the same name and set the DOM property
- propName = jQuery.propFix[ name ] || name;
- if ( propName in elem ) {
- // Only set the IDL specifically if it already exists on the element
- elem[ propName ] = true;
- }
-
- elem.setAttribute( name, name.toLowerCase() );
- }
- return name;
- }
-};
-
-// IE6/7 do not support getting/setting some attributes with get/setAttribute
-if ( !jQuery.support.getSetAttribute ) {
-
- // propFix is more comprehensive and contains all fixes
- jQuery.attrFix = jQuery.propFix;
-
- // Use this for any attribute on a form in IE6/7
- formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = jQuery.valHooks.button = {
- get: function( elem, name ) {
- var ret;
- ret = elem.getAttributeNode( name );
- // Return undefined if nodeValue is empty string
- return ret && ret.nodeValue !== "" ?
- ret.nodeValue :
- undefined;
- },
- set: function( elem, value, name ) {
- // Check form objects in IE (multiple bugs related)
- // Only use nodeValue if the attribute node exists on the form
- var ret = elem.getAttributeNode( name );
- if ( ret ) {
- ret.nodeValue = value;
- return value;
- }
- }
- };
-
- // Set width and height to auto instead of 0 on empty string( Bug #8150 )
- // This is for removals
- jQuery.each([ "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
- set: function( elem, value ) {
- if ( value === "" ) {
- elem.setAttribute( name, "auto" );
- return value;
- }
- }
- });
- });
-}
-
-
-// Some attributes require a special call on IE
-if ( !jQuery.support.hrefNormalized ) {
- jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
- get: function( elem ) {
- var ret = elem.getAttribute( name, 2 );
- return ret === null ? undefined : ret;
- }
- });
- });
-}
-
-if ( !jQuery.support.style ) {
- jQuery.attrHooks.style = {
- get: function( elem ) {
- // Return undefined in the case of empty string
- // Normalize to lowercase since IE uppercases css property names
- return elem.style.cssText.toLowerCase() || undefined;
- },
- set: function( elem, value ) {
- return (elem.style.cssText = "" + value);
- }
- };
-}
-
-// Safari mis-reports the default selected property of an option
-// Accessing the parent's selectedIndex property fixes it
-if ( !jQuery.support.optSelected ) {
- jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
- get: function( elem ) {
- var parent = elem.parentNode;
-
- if ( parent ) {
- parent.selectedIndex;
-
- // Make sure that it also works with optgroups, see #5701
- if ( parent.parentNode ) {
- parent.parentNode.selectedIndex;
- }
- }
- }
- });
-}
-
-// Radios and checkboxes getter/setter
-if ( !jQuery.support.checkOn ) {
- jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = {
- get: function( elem ) {
- // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
- return elem.getAttribute("value") === null ? "on" : elem.value;
- }
- };
- });
-}
-jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
- set: function( elem, value ) {
- if ( jQuery.isArray( value ) ) {
- return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
- }
- }
- });
-});
-
-
-
-
-var rnamespaces = /\.(.*)$/,
- rformElems = /^(?:textarea|input|select)$/i,
- rperiod = /\./g,
- rspaces = / /g,
- rescape = /[^\w\s.|`]/g,
- fcleanup = function( nm ) {
- return nm.replace(rescape, "\\$&");
- };
-
-/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code originated from
- * Dean Edwards' addEvent library.
- */
-jQuery.event = {
-
- // Bind an event to an element
- // Original by Dean Edwards
- add: function( elem, types, handler, data ) {
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- if ( handler === false ) {
- handler = returnFalse;
- } else if ( !handler ) {
- // Fixes bug #7229. Fix recommended by jdalton
- return;
- }
-
- var handleObjIn, handleObj;
-
- if ( handler.handler ) {
- handleObjIn = handler;
- handler = handleObjIn.handler;
- }
-
- // Make sure that the function being executed has a unique ID
- if ( !handler.guid ) {
- handler.guid = jQuery.guid++;
- }
-
- // Init the element's event structure
- var elemData = jQuery._data( elem );
-
- // If no elemData is found then we must be trying to bind to one of the
- // banned noData elements
- if ( !elemData ) {
- return;
- }
-
- var events = elemData.events,
- eventHandle = elemData.handle;
-
- if ( !events ) {
- elemData.events = events = {};
- }
-
- if ( !eventHandle ) {
- elemData.handle = eventHandle = function( e ) {
- // Discard the second event of a jQuery.event.trigger() and
- // when an event is called after a page has unloaded
- return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.handle.apply( eventHandle.elem, arguments ) :
- undefined;
- };
- }
-
- // Add elem as a property of the handle function
- // This is to prevent a memory leak with non-native events in IE.
- eventHandle.elem = elem;
-
- // Handle multiple events separated by a space
- // jQuery(...).bind("mouseover mouseout", fn);
- types = types.split(" ");
-
- var type, i = 0, namespaces;
-
- while ( (type = types[ i++ ]) ) {
- handleObj = handleObjIn ?
- jQuery.extend({}, handleObjIn) :
- { handler: handler, data: data };
-
- // Namespaced event handlers
- if ( type.indexOf(".") > -1 ) {
- namespaces = type.split(".");
- type = namespaces.shift();
- handleObj.namespace = namespaces.slice(0).sort().join(".");
-
- } else {
- namespaces = [];
- handleObj.namespace = "";
- }
-
- handleObj.type = type;
- if ( !handleObj.guid ) {
- handleObj.guid = handler.guid;
- }
-
- // Get the current list of functions bound to this event
- var handlers = events[ type ],
- special = jQuery.event.special[ type ] || {};
-
- // Init the event handler queue
- if ( !handlers ) {
- handlers = events[ type ] = [];
-
- // Check for a special event handler
- // Only use addEventListener/attachEvent if the special
- // events handler returns false
- if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
- // Bind the global event handler to the element
- if ( elem.addEventListener ) {
- elem.addEventListener( type, eventHandle, false );
-
- } else if ( elem.attachEvent ) {
- elem.attachEvent( "on" + type, eventHandle );
- }
- }
- }
-
- if ( special.add ) {
- special.add.call( elem, handleObj );
-
- if ( !handleObj.handler.guid ) {
- handleObj.handler.guid = handler.guid;
- }
- }
-
- // Add the function to the element's handler list
- handlers.push( handleObj );
-
- // Keep track of which events have been used, for event optimization
- jQuery.event.global[ type ] = true;
- }
-
- // Nullify elem to prevent memory leaks in IE
- elem = null;
- },
-
- global: {},
-
- // Detach an event or set of events from an element
- remove: function( elem, types, handler, pos ) {
- // don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- if ( handler === false ) {
- handler = returnFalse;
- }
-
- var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
- elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
- events = elemData && elemData.events;
-
- if ( !elemData || !events ) {
- return;
- }
-
- // types is actually an event object here
- if ( types && types.type ) {
- handler = types.handler;
- types = types.type;
- }
-
- // Unbind all events for the element
- if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
- types = types || "";
-
- for ( type in events ) {
- jQuery.event.remove( elem, type + types );
- }
-
- return;
- }
-
- // Handle multiple events separated by a space
- // jQuery(...).unbind("mouseover mouseout", fn);
- types = types.split(" ");
-
- while ( (type = types[ i++ ]) ) {
- origType = type;
- handleObj = null;
- all = type.indexOf(".") < 0;
- namespaces = [];
-
- if ( !all ) {
- // Namespaced event handlers
- namespaces = type.split(".");
- type = namespaces.shift();
-
- namespace = new RegExp("(^|\\.)" +
- jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- eventType = events[ type ];
-
- if ( !eventType ) {
- continue;
- }
-
- if ( !handler ) {
- for ( j = 0; j < eventType.length; j++ ) {
- handleObj = eventType[ j ];
-
- if ( all || namespace.test( handleObj.namespace ) ) {
- jQuery.event.remove( elem, origType, handleObj.handler, j );
- eventType.splice( j--, 1 );
- }
- }
-
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
-
- for ( j = pos || 0; j < eventType.length; j++ ) {
- handleObj = eventType[ j ];
-
- if ( handler.guid === handleObj.guid ) {
- // remove the given handler for the given type
- if ( all || namespace.test( handleObj.namespace ) ) {
- if ( pos == null ) {
- eventType.splice( j--, 1 );
- }
-
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
- }
-
- if ( pos != null ) {
- break;
- }
- }
- }
-
- // remove generic event handler if no more handlers exist
- if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
- if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
- jQuery.removeEvent( elem, type, elemData.handle );
- }
-
- ret = null;
- delete events[ type ];
- }
- }
-
- // Remove the expando if it's no longer used
- if ( jQuery.isEmptyObject( events ) ) {
- var handle = elemData.handle;
- if ( handle ) {
- handle.elem = null;
- }
-
- delete elemData.events;
- delete elemData.handle;
-
- if ( jQuery.isEmptyObject( elemData ) ) {
- jQuery.removeData( elem, undefined, true );
- }
- }
- },
-
- // Events that are safe to short-circuit if no handlers are attached.
- // Native DOM events should not be added, they may have inline handlers.
- customEvent: {
- "getData": true,
- "setData": true,
- "changeData": true
- },
-
- trigger: function( event, data, elem, onlyHandlers ) {
- // Event object or event type
- var type = event.type || event,
- namespaces = [],
- exclusive;
-
- if ( type.indexOf("!") >= 0 ) {
- // Exclusive events trigger only for the exact event (no namespaces)
- type = type.slice(0, -1);
- exclusive = true;
- }
-
- if ( type.indexOf(".") >= 0 ) {
- // Namespaced trigger; create a regexp to match event type in handle()
- namespaces = type.split(".");
- type = namespaces.shift();
- namespaces.sort();
- }
-
- if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
- // No jQuery handlers for this event type, and it can't have inline handlers
- return;
- }
-
- // Caller can pass in an Event, Object, or just an event type string
- event = typeof event === "object" ?
- // jQuery.Event object
- event[ jQuery.expando ] ? event :
- // Object literal
- new jQuery.Event( type, event ) :
- // Just the event type (string)
- new jQuery.Event( type );
-
- event.type = type;
- event.exclusive = exclusive;
- event.namespace = namespaces.join(".");
- event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
-
- // triggerHandler() and global events don't bubble or run the default action
- if ( onlyHandlers || !elem ) {
- event.preventDefault();
- event.stopPropagation();
- }
-
- // Handle a global trigger
- if ( !elem ) {
- // TODO: Stop taunting the data cache; remove global events and always attach to document
- jQuery.each( jQuery.cache, function() {
- // internalKey variable is just used to make it easier to find
- // and potentially change this stuff later; currently it just
- // points to jQuery.expando
- var internalKey = jQuery.expando,
- internalCache = this[ internalKey ];
- if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
- jQuery.event.trigger( event, data, internalCache.handle.elem );
- }
- });
- return;
- }
-
- // Don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- // Clean up the event in case it is being reused
- event.result = undefined;
- event.target = elem;
-
- // Clone any incoming data and prepend the event, creating the handler arg list
- data = data != null ? jQuery.makeArray( data ) : [];
- data.unshift( event );
-
- var cur = elem,
- // IE doesn't like method names with a colon (#3533, #8272)
- ontype = type.indexOf(":") < 0 ? "on" + type : "";
-
- // Fire event on the current element, then bubble up the DOM tree
- do {
- var handle = jQuery._data( cur, "handle" );
-
- event.currentTarget = cur;
- if ( handle ) {
- handle.apply( cur, data );
- }
-
- // Trigger an inline bound script
- if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
- event.result = false;
- event.preventDefault();
- }
-
- // Bubble up to document, then to window
- cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
- } while ( cur && !event.isPropagationStopped() );
-
- // If nobody prevented the default action, do it now
- if ( !event.isDefaultPrevented() ) {
- var old,
- special = jQuery.event.special[ type ] || {};
-
- if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
- !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
-
- // Call a native DOM method on the target with the same name name as the event.
- // Can't use an .isFunction)() check here because IE6/7 fails that test.
- // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
- try {
- if ( ontype && elem[ type ] ) {
- // Don't re-trigger an onFOO event when we call its FOO() method
- old = elem[ ontype ];
-
- if ( old ) {
- elem[ ontype ] = null;
- }
-
- jQuery.event.triggered = type;
- elem[ type ]();
- }
- } catch ( ieError ) {}
-
- if ( old ) {
- elem[ ontype ] = old;
- }
-
- jQuery.event.triggered = undefined;
- }
- }
-
- return event.result;
- },
-
- handle: function( event ) {
- event = jQuery.event.fix( event || window.event );
- // Snapshot the handlers list since a called handler may add/remove events.
- var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
- run_all = !event.exclusive && !event.namespace,
- args = Array.prototype.slice.call( arguments, 0 );
-
- // Use the fix-ed Event rather than the (read-only) native event
- args[0] = event;
- event.currentTarget = this;
-
- for ( var j = 0, l = handlers.length; j < l; j++ ) {
- var handleObj = handlers[ j ];
-
- // Triggered event must 1) be non-exclusive and have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event.
- if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
- // Pass in a reference to the handler function itself
- // So that we can later remove it
- event.handler = handleObj.handler;
- event.data = handleObj.data;
- event.handleObj = handleObj;
-
- var ret = handleObj.handler.apply( this, args );
-
- if ( ret !== undefined ) {
- event.result = ret;
- if ( ret === false ) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
-
- if ( event.isImmediatePropagationStopped() ) {
- break;
- }
- }
- }
- return event.result;
- },
-
- props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-
- fix: function( event ) {
- if ( event[ jQuery.expando ] ) {
- return event;
- }
-
- // store a copy of the original event object
- // and "clone" to set read-only properties
- var originalEvent = event;
- event = jQuery.Event( originalEvent );
-
- for ( var i = this.props.length, prop; i; ) {
- prop = this.props[ --i ];
- event[ prop ] = originalEvent[ prop ];
- }
-
- // Fix target property, if necessary
- if ( !event.target ) {
- // Fixes #1925 where srcElement might not be defined either
- event.target = event.srcElement || document;
- }
-
- // check if target is a textnode (safari)
- if ( event.target.nodeType === 3 ) {
- event.target = event.target.parentNode;
- }
-
- // Add relatedTarget, if necessary
- if ( !event.relatedTarget && event.fromElement ) {
- event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
- }
-
- // Calculate pageX/Y if missing and clientX/Y available
- if ( event.pageX == null && event.clientX != null ) {
- var eventDocument = event.target.ownerDocument || document,
- doc = eventDocument.documentElement,
- body = eventDocument.body;
-
- event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
- event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
- }
-
- // Add which for key events
- if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
- event.which = event.charCode != null ? event.charCode : event.keyCode;
- }
-
- // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
- if ( !event.metaKey && event.ctrlKey ) {
- event.metaKey = event.ctrlKey;
- }
-
- // Add which for click: 1 === left; 2 === middle; 3 === right
- // Note: button is not normalized, so don't use it
- if ( !event.which && event.button !== undefined ) {
- event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
- }
-
- return event;
- },
-
- // Deprecated, use jQuery.guid instead
- guid: 1E8,
-
- // Deprecated, use jQuery.proxy instead
- proxy: jQuery.proxy,
-
- special: {
- ready: {
- // Make sure the ready event is setup
- setup: jQuery.bindReady,
- teardown: jQuery.noop
- },
-
- live: {
- add: function( handleObj ) {
- jQuery.event.add( this,
- liveConvert( handleObj.origType, handleObj.selector ),
- jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
- },
-
- remove: function( handleObj ) {
- jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
- }
- },
-
- beforeunload: {
- setup: function( data, namespaces, eventHandle ) {
- // We only want to do this special case on windows
- if ( jQuery.isWindow( this ) ) {
- this.onbeforeunload = eventHandle;
- }
- },
-
- teardown: function( namespaces, eventHandle ) {
- if ( this.onbeforeunload === eventHandle ) {
- this.onbeforeunload = null;
- }
- }
- }
- }
-};
-
-jQuery.removeEvent = document.removeEventListener ?
- function( elem, type, handle ) {
- if ( elem.removeEventListener ) {
- elem.removeEventListener( type, handle, false );
- }
- } :
- function( elem, type, handle ) {
- if ( elem.detachEvent ) {
- elem.detachEvent( "on" + type, handle );
- }
- };
-
-jQuery.Event = function( src, props ) {
- // Allow instantiation without the 'new' keyword
- if ( !this.preventDefault ) {
- return new jQuery.Event( src, props );
- }
-
- // Event object
- if ( src && src.type ) {
- this.originalEvent = src;
- this.type = src.type;
-
- // Events bubbling up the document may have been marked as prevented
- // by a handler lower down the tree; reflect the correct value.
- this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
- src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
-
- // Event type
- } else {
- this.type = src;
- }
-
- // Put explicitly provided properties onto the event object
- if ( props ) {
- jQuery.extend( this, props );
- }
-
- // timeStamp is buggy for some events on Firefox(#3843)
- // So we won't rely on the native value
- this.timeStamp = jQuery.now();
-
- // Mark it as fixed
- this[ jQuery.expando ] = true;
-};
-
-function returnFalse() {
- return false;
-}
-function returnTrue() {
- return true;
-}
-
-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-jQuery.Event.prototype = {
- preventDefault: function() {
- this.isDefaultPrevented = returnTrue;
-
- var e = this.originalEvent;
- if ( !e ) {
- return;
- }
-
- // if preventDefault exists run it on the original event
- if ( e.preventDefault ) {
- e.preventDefault();
-
- // otherwise set the returnValue property of the original event to false (IE)
- } else {
- e.returnValue = false;
- }
- },
- stopPropagation: function() {
- this.isPropagationStopped = returnTrue;
-
- var e = this.originalEvent;
- if ( !e ) {
- return;
- }
- // if stopPropagation exists run it on the original event
- if ( e.stopPropagation ) {
- e.stopPropagation();
- }
- // otherwise set the cancelBubble property of the original event to true (IE)
- e.cancelBubble = true;
- },
- stopImmediatePropagation: function() {
- this.isImmediatePropagationStopped = returnTrue;
- this.stopPropagation();
- },
- isDefaultPrevented: returnFalse,
- isPropagationStopped: returnFalse,
- isImmediatePropagationStopped: returnFalse
-};
-
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function( event ) {
-
- // Check if mouse(over|out) are still within the same parent element
- var related = event.relatedTarget,
- inside = false,
- eventType = event.type;
-
- event.type = event.data;
-
- if ( related !== this ) {
-
- if ( related ) {
- inside = jQuery.contains( this, related );
- }
-
- if ( !inside ) {
-
- jQuery.event.handle.apply( this, arguments );
-
- event.type = eventType;
- }
- }
-},
-
-// In case of event delegation, we only need to rename the event.type,
-// liveHandler will take care of the rest.
-delegate = function( event ) {
- event.type = event.data;
- jQuery.event.handle.apply( this, arguments );
-};
-
-// Create mouseenter and mouseleave events
-jQuery.each({
- mouseenter: "mouseover",
- mouseleave: "mouseout"
-}, function( orig, fix ) {
- jQuery.event.special[ orig ] = {
- setup: function( data ) {
- jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
- },
- teardown: function( data ) {
- jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
- }
- };
-});
-
-// submit delegation
-if ( !jQuery.support.submitBubbles ) {
-
- jQuery.event.special.submit = {
- setup: function( data, namespaces ) {
- if ( !jQuery.nodeName( this, "form" ) ) {
- jQuery.event.add(this, "click.specialSubmit", function( e ) {
- var elem = e.target,
- type = elem.type;
-
- if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
- trigger( "submit", this, arguments );
- }
- });
-
- jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
- var elem = e.target,
- type = elem.type;
-
- if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
- trigger( "submit", this, arguments );
- }
- });
-
- } else {
- return false;
- }
- },
-
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialSubmit" );
- }
- };
-
-}
-
-// change delegation, happens here so we have bind.
-if ( !jQuery.support.changeBubbles ) {
-
- var changeFilters,
-
- getVal = function( elem ) {
- var type = elem.type, val = elem.value;
-
- if ( type === "radio" || type === "checkbox" ) {
- val = elem.checked;
-
- } else if ( type === "select-multiple" ) {
- val = elem.selectedIndex > -1 ?
- jQuery.map( elem.options, function( elem ) {
- return elem.selected;
- }).join("-") :
- "";
-
- } else if ( jQuery.nodeName( elem, "select" ) ) {
- val = elem.selectedIndex;
- }
-
- return val;
- },
-
- testChange = function testChange( e ) {
- var elem = e.target, data, val;
-
- if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
- return;
- }
-
- data = jQuery._data( elem, "_change_data" );
- val = getVal(elem);
-
- // the current data will be also retrieved by beforeactivate
- if ( e.type !== "focusout" || elem.type !== "radio" ) {
- jQuery._data( elem, "_change_data", val );
- }
-
- if ( data === undefined || val === data ) {
- return;
- }
-
- if ( data != null || val ) {
- e.type = "change";
- e.liveFired = undefined;
- jQuery.event.trigger( e, arguments[1], elem );
- }
- };
-
- jQuery.event.special.change = {
- filters: {
- focusout: testChange,
-
- beforedeactivate: testChange,
-
- click: function( e ) {
- var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
-
- if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
- testChange.call( this, e );
- }
- },
-
- // Change has to be called before submit
- // Keydown will be called before keypress, which is used in submit-event delegation
- keydown: function( e ) {
- var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
-
- if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
- (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
- type === "select-multiple" ) {
- testChange.call( this, e );
- }
- },
-
- // Beforeactivate happens also before the previous element is blurred
- // with this event you can't trigger a change event, but you can store
- // information
- beforeactivate: function( e ) {
- var elem = e.target;
- jQuery._data( elem, "_change_data", getVal(elem) );
- }
- },
-
- setup: function( data, namespaces ) {
- if ( this.type === "file" ) {
- return false;
- }
-
- for ( var type in changeFilters ) {
- jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
- }
-
- return rformElems.test( this.nodeName );
- },
-
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialChange" );
-
- return rformElems.test( this.nodeName );
- }
- };
-
- changeFilters = jQuery.event.special.change.filters;
-
- // Handle when the input is .focus()'d
- changeFilters.focus = changeFilters.beforeactivate;
-}
-
-function trigger( type, elem, args ) {
- // Piggyback on a donor event to simulate a different one.
- // Fake originalEvent to avoid donor's stopPropagation, but if the
- // simulated event prevents default then we do the same on the donor.
- // Don't pass args or remember liveFired; they apply to the donor event.
- var event = jQuery.extend( {}, args[ 0 ] );
- event.type = type;
- event.originalEvent = {};
- event.liveFired = undefined;
- jQuery.event.handle.call( elem, event );
- if ( event.isDefaultPrevented() ) {
- args[ 0 ].preventDefault();
- }
-}
-
-// Create "bubbling" focus and blur events
-if ( !jQuery.support.focusinBubbles ) {
- jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
-
- // Attach a single capturing handler while someone wants focusin/focusout
- var attaches = 0;
-
- jQuery.event.special[ fix ] = {
- setup: function() {
- if ( attaches++ === 0 ) {
- document.addEventListener( orig, handler, true );
- }
- },
- teardown: function() {
- if ( --attaches === 0 ) {
- document.removeEventListener( orig, handler, true );
- }
- }
- };
-
- function handler( donor ) {
- // Donor event is always a native one; fix it and switch its type.
- // Let focusin/out handler cancel the donor focus/blur event.
- var e = jQuery.event.fix( donor );
- e.type = fix;
- e.originalEvent = {};
- jQuery.event.trigger( e, null, e.target );
- if ( e.isDefaultPrevented() ) {
- donor.preventDefault();
- }
- }
- });
-}
-
-jQuery.each(["bind", "one"], function( i, name ) {
- jQuery.fn[ name ] = function( type, data, fn ) {
- var handler;
-
- // Handle object literals
- if ( typeof type === "object" ) {
- for ( var key in type ) {
- this[ name ](key, data, type[key], fn);
- }
- return this;
- }
-
- if ( arguments.length === 2 || data === false ) {
- fn = data;
- data = undefined;
- }
-
- if ( name === "one" ) {
- handler = function( event ) {
- jQuery( this ).unbind( event, handler );
- return fn.apply( this, arguments );
- };
- handler.guid = fn.guid || jQuery.guid++;
- } else {
- handler = fn;
- }
-
- if ( type === "unload" && name !== "one" ) {
- this.one( type, data, fn );
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- jQuery.event.add( this[i], type, handler, data );
- }
- }
-
- return this;
- };
-});
-
-jQuery.fn.extend({
- unbind: function( type, fn ) {
- // Handle object literals
- if ( typeof type === "object" && !type.preventDefault ) {
- for ( var key in type ) {
- this.unbind(key, type[key]);
- }
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- jQuery.event.remove( this[i], type, fn );
- }
- }
-
- return this;
- },
-
- delegate: function( selector, types, data, fn ) {
- return this.live( types, data, fn, selector );
- },
-
- undelegate: function( selector, types, fn ) {
- if ( arguments.length === 0 ) {
- return this.unbind( "live" );
-
- } else {
- return this.die( types, null, fn, selector );
- }
- },
-
- trigger: function( type, data ) {
- return this.each(function() {
- jQuery.event.trigger( type, data, this );
- });
- },
-
- triggerHandler: function( type, data ) {
- if ( this[0] ) {
- return jQuery.event.trigger( type, data, this[0], true );
- }
- },
-
- toggle: function( fn ) {
- // Save reference to arguments for access in closure
- var args = arguments,
- guid = fn.guid || jQuery.guid++,
- i = 0,
- toggler = function( event ) {
- // Figure out which function to execute
- var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
- jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
-
- // Make sure that clicks stop
- event.preventDefault();
-
- // and execute the function
- return args[ lastToggle ].apply( this, arguments ) || false;
- };
-
- // link all the functions, so any of them can unbind this click handler
- toggler.guid = guid;
- while ( i < args.length ) {
- args[ i++ ].guid = guid;
- }
-
- return this.click( toggler );
- },
-
- hover: function( fnOver, fnOut ) {
- return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
- }
-});
-
-var liveMap = {
- focus: "focusin",
- blur: "focusout",
- mouseenter: "mouseover",
- mouseleave: "mouseout"
-};
-
-jQuery.each(["live", "die"], function( i, name ) {
- jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
- var type, i = 0, match, namespaces, preType,
- selector = origSelector || this.selector,
- context = origSelector ? this : jQuery( this.context );
-
- if ( typeof types === "object" && !types.preventDefault ) {
- for ( var key in types ) {
- context[ name ]( key, data, types[key], selector );
- }
-
- return this;
- }
-
- if ( name === "die" && !types &&
- origSelector && origSelector.charAt(0) === "." ) {
-
- context.unbind( origSelector );
-
- return this;
- }
-
- if ( data === false || jQuery.isFunction( data ) ) {
- fn = data || returnFalse;
- data = undefined;
- }
-
- types = (types || "").split(" ");
-
- while ( (type = types[ i++ ]) != null ) {
- match = rnamespaces.exec( type );
- namespaces = "";
-
- if ( match ) {
- namespaces = match[0];
- type = type.replace( rnamespaces, "" );
- }
-
- if ( type === "hover" ) {
- types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
- continue;
- }
-
- preType = type;
-
- if ( liveMap[ type ] ) {
- types.push( liveMap[ type ] + namespaces );
- type = type + namespaces;
-
- } else {
- type = (liveMap[ type ] || type) + namespaces;
- }
-
- if ( name === "live" ) {
- // bind live handler
- for ( var j = 0, l = context.length; j < l; j++ ) {
- jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
- { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
- }
-
- } else {
- // unbind live handler
- context.unbind( "live." + liveConvert( type, selector ), fn );
- }
- }
-
- return this;
- };
-});
-
-function liveHandler( event ) {
- var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
- elems = [],
- selectors = [],
- events = jQuery._data( this, "events" );
-
- // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
- if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
- return;
- }
-
- if ( event.namespace ) {
- namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- event.liveFired = this;
-
- var live = events.live.slice(0);
-
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
- selectors.push( handleObj.selector );
-
- } else {
- live.splice( j--, 1 );
- }
- }
-
- match = jQuery( event.target ).closest( selectors, event.currentTarget );
-
- for ( i = 0, l = match.length; i < l; i++ ) {
- close = match[i];
-
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
- elem = close.elem;
- related = null;
-
- // Those two events require additional checking
- if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
- event.type = handleObj.preType;
- related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
-
- // Make sure not to accidentally match a child element with the same selector
- if ( related && jQuery.contains( elem, related ) ) {
- related = elem;
- }
- }
-
- if ( !related || related !== elem ) {
- elems.push({ elem: elem, handleObj: handleObj, level: close.level });
- }
- }
- }
- }
-
- for ( i = 0, l = elems.length; i < l; i++ ) {
- match = elems[i];
-
- if ( maxLevel && match.level > maxLevel ) {
- break;
- }
-
- event.currentTarget = match.elem;
- event.data = match.handleObj.data;
- event.handleObj = match.handleObj;
-
- ret = match.handleObj.origHandler.apply( match.elem, arguments );
-
- if ( ret === false || event.isPropagationStopped() ) {
- maxLevel = match.level;
-
- if ( ret === false ) {
- stop = false;
- }
- if ( event.isImmediatePropagationStopped() ) {
- break;
- }
- }
- }
-
- return stop;
-}
-
-function liveConvert( type, selector ) {
- return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&");
-}
-
-jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
- "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
- "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
-
- // Handle event binding
- jQuery.fn[ name ] = function( data, fn ) {
- if ( fn == null ) {
- fn = data;
- data = null;
- }
-
- return arguments.length > 0 ?
- this.bind( name, data, fn ) :
- this.trigger( name );
- };
-
- if ( jQuery.attrFn ) {
- jQuery.attrFn[ name ] = true;
- }
-});
-
-
-
-/*!
- * Sizzle CSS Selector Engine
- * Copyright 2011, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- * More information: http://sizzlejs.com/
- */
-(function(){
-
-var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
- done = 0,
- toString = Object.prototype.toString,
- hasDuplicate = false,
- baseHasDuplicate = true,
- rBackslash = /\\/g,
- rNonWord = /\W/;
-
-// Here we check if the JavaScript engine is using some sort of
-// optimization where it does not always call our comparision
-// function. If that is the case, discard the hasDuplicate value.
-// Thus far that includes Google Chrome.
-[0, 0].sort(function() {
- baseHasDuplicate = false;
- return 0;
-});
-
-var Sizzle = function( selector, context, results, seed ) {
- results = results || [];
- context = context || document;
-
- var origContext = context;
-
- if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
- return [];
- }
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- var m, set, checkSet, extra, ret, cur, pop, i,
- prune = true,
- contextXML = Sizzle.isXML( context ),
- parts = [],
- soFar = selector;
-
- // Reset the position of the chunker regexp (start from head)
- do {
- chunker.exec( "" );
- m = chunker.exec( soFar );
-
- if ( m ) {
- soFar = m[3];
-
- parts.push( m[1] );
-
- if ( m[2] ) {
- extra = m[3];
- break;
- }
- }
- } while ( m );
-
- if ( parts.length > 1 && origPOS.exec( selector ) ) {
-
- if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- set = posProcess( parts[0] + parts[1], context );
-
- } else {
- set = Expr.relative[ parts[0] ] ?
- [ context ] :
- Sizzle( parts.shift(), context );
-
- while ( parts.length ) {
- selector = parts.shift();
-
- if ( Expr.relative[ selector ] ) {
- selector += parts.shift();
- }
-
- set = posProcess( selector, set );
- }
- }
-
- } else {
- // Take a shortcut and set the context if the root selector is an ID
- // (but not if it'll be faster if the inner selector is an ID)
- if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
- Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
-
- ret = Sizzle.find( parts.shift(), context, contextXML );
- context = ret.expr ?
- Sizzle.filter( ret.expr, ret.set )[0] :
- ret.set[0];
- }
-
- if ( context ) {
- ret = seed ?
- { expr: parts.pop(), set: makeArray(seed) } :
- Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
-
- set = ret.expr ?
- Sizzle.filter( ret.expr, ret.set ) :
- ret.set;
-
- if ( parts.length > 0 ) {
- checkSet = makeArray( set );
-
- } else {
- prune = false;
- }
-
- while ( parts.length ) {
- cur = parts.pop();
- pop = cur;
-
- if ( !Expr.relative[ cur ] ) {
- cur = "";
- } else {
- pop = parts.pop();
- }
-
- if ( pop == null ) {
- pop = context;
- }
-
- Expr.relative[ cur ]( checkSet, pop, contextXML );
- }
-
- } else {
- checkSet = parts = [];
- }
- }
-
- if ( !checkSet ) {
- checkSet = set;
- }
-
- if ( !checkSet ) {
- Sizzle.error( cur || selector );
- }
-
- if ( toString.call(checkSet) === "[object Array]" ) {
- if ( !prune ) {
- results.push.apply( results, checkSet );
-
- } else if ( context && context.nodeType === 1 ) {
- for ( i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
- results.push( set[i] );
- }
- }
-
- } else {
- for ( i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
- results.push( set[i] );
- }
- }
- }
-
- } else {
- makeArray( checkSet, results );
- }
-
- if ( extra ) {
- Sizzle( extra, origContext, results, seed );
- Sizzle.uniqueSort( results );
- }
-
- return results;
-};
-
-Sizzle.uniqueSort = function( results ) {
- if ( sortOrder ) {
- hasDuplicate = baseHasDuplicate;
- results.sort( sortOrder );
-
- if ( hasDuplicate ) {
- for ( var i = 1; i < results.length; i++ ) {
- if ( results[i] === results[ i - 1 ] ) {
- results.splice( i--, 1 );
- }
- }
- }
- }
-
- return results;
-};
-
-Sizzle.matches = function( expr, set ) {
- return Sizzle( expr, null, null, set );
-};
-
-Sizzle.matchesSelector = function( node, expr ) {
- return Sizzle( expr, null, null, [node] ).length > 0;
-};
-
-Sizzle.find = function( expr, context, isXML ) {
- var set;
-
- if ( !expr ) {
- return [];
- }
-
- for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
- var match,
- type = Expr.order[i];
-
- if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
- var left = match[1];
- match.splice( 1, 1 );
-
- if ( left.substr( left.length - 1 ) !== "\\" ) {
- match[1] = (match[1] || "").replace( rBackslash, "" );
- set = Expr.find[ type ]( match, context, isXML );
-
- if ( set != null ) {
- expr = expr.replace( Expr.match[ type ], "" );
- break;
- }
- }
- }
- }
-
- if ( !set ) {
- set = typeof context.getElementsByTagName !== "undefined" ?
- context.getElementsByTagName( "*" ) :
- [];
- }
-
- return { set: set, expr: expr };
-};
-
-Sizzle.filter = function( expr, set, inplace, not ) {
- var match, anyFound,
- old = expr,
- result = [],
- curLoop = set,
- isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
-
- while ( expr && set.length ) {
- for ( var type in Expr.filter ) {
- if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
- var found, item,
- filter = Expr.filter[ type ],
- left = match[1];
-
- anyFound = false;
-
- match.splice(1,1);
-
- if ( left.substr( left.length - 1 ) === "\\" ) {
- continue;
- }
-
- if ( curLoop === result ) {
- result = [];
- }
-
- if ( Expr.preFilter[ type ] ) {
- match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
-
- if ( !match ) {
- anyFound = found = true;
-
- } else if ( match === true ) {
- continue;
- }
- }
-
- if ( match ) {
- for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
- if ( item ) {
- found = filter( item, match, i, curLoop );
- var pass = not ^ !!found;
-
- if ( inplace && found != null ) {
- if ( pass ) {
- anyFound = true;
-
- } else {
- curLoop[i] = false;
- }
-
- } else if ( pass ) {
- result.push( item );
- anyFound = true;
- }
- }
- }
- }
-
- if ( found !== undefined ) {
- if ( !inplace ) {
- curLoop = result;
- }
-
- expr = expr.replace( Expr.match[ type ], "" );
-
- if ( !anyFound ) {
- return [];
- }
-
- break;
- }
- }
- }
-
- // Improper expression
- if ( expr === old ) {
- if ( anyFound == null ) {
- Sizzle.error( expr );
-
- } else {
- break;
- }
- }
-
- old = expr;
- }
-
- return curLoop;
-};
-
-Sizzle.error = function( msg ) {
- throw "Syntax error, unrecognized expression: " + msg;
-};
-
-var Expr = Sizzle.selectors = {
- order: [ "ID", "NAME", "TAG" ],
-
- match: {
- ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
- CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
- NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
- TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
- POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
- PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
- },
-
- leftMatch: {},
-
- attrMap: {
- "class": "className",
- "for": "htmlFor"
- },
-
- attrHandle: {
- href: function( elem ) {
- return elem.getAttribute( "href" );
- },
- type: function( elem ) {
- return elem.getAttribute( "type" );
- }
- },
-
- relative: {
- "+": function(checkSet, part){
- var isPartStr = typeof part === "string",
- isTag = isPartStr && !rNonWord.test( part ),
- isPartStrNotTag = isPartStr && !isTag;
-
- if ( isTag ) {
- part = part.toLowerCase();
- }
-
- for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
- if ( (elem = checkSet[i]) ) {
- while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
-
- checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
- elem || false :
- elem === part;
- }
- }
-
- if ( isPartStrNotTag ) {
- Sizzle.filter( part, checkSet, true );
- }
- },
-
- ">": function( checkSet, part ) {
- var elem,
- isPartStr = typeof part === "string",
- i = 0,
- l = checkSet.length;
-
- if ( isPartStr && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
-
- for ( ; i < l; i++ ) {
- elem = checkSet[i];
-
- if ( elem ) {
- var parent = elem.parentNode;
- checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
- }
- }
-
- } else {
- for ( ; i < l; i++ ) {
- elem = checkSet[i];
-
- if ( elem ) {
- checkSet[i] = isPartStr ?
- elem.parentNode :
- elem.parentNode === part;
- }
- }
-
- if ( isPartStr ) {
- Sizzle.filter( part, checkSet, true );
- }
- }
- },
-
- "": function(checkSet, part, isXML){
- var nodeCheck,
- doneName = done++,
- checkFn = dirCheck;
-
- if ( typeof part === "string" && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
- nodeCheck = part;
- checkFn = dirNodeCheck;
- }
-
- checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
- },
-
- "~": function( checkSet, part, isXML ) {
- var nodeCheck,
- doneName = done++,
- checkFn = dirCheck;
-
- if ( typeof part === "string" && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
- nodeCheck = part;
- checkFn = dirNodeCheck;
- }
-
- checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
- }
- },
-
- find: {
- ID: function( match, context, isXML ) {
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [m] : [];
- }
- },
-
- NAME: function( match, context ) {
- if ( typeof context.getElementsByName !== "undefined" ) {
- var ret = [],
- results = context.getElementsByName( match[1] );
-
- for ( var i = 0, l = results.length; i < l; i++ ) {
- if ( results[i].getAttribute("name") === match[1] ) {
- ret.push( results[i] );
- }
- }
-
- return ret.length === 0 ? null : ret;
- }
- },
-
- TAG: function( match, context ) {
- if ( typeof context.getElementsByTagName !== "undefined" ) {
- return context.getElementsByTagName( match[1] );
- }
- }
- },
- preFilter: {
- CLASS: function( match, curLoop, inplace, result, not, isXML ) {
- match = " " + match[1].replace( rBackslash, "" ) + " ";
-
- if ( isXML ) {
- return match;
- }
-
- for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
- if ( elem ) {
- if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
- if ( !inplace ) {
- result.push( elem );
- }
-
- } else if ( inplace ) {
- curLoop[i] = false;
- }
- }
- }
-
- return false;
- },
-
- ID: function( match ) {
- return match[1].replace( rBackslash, "" );
- },
-
- TAG: function( match, curLoop ) {
- return match[1].replace( rBackslash, "" ).toLowerCase();
- },
-
- CHILD: function( match ) {
- if ( match[1] === "nth" ) {
- if ( !match[2] ) {
- Sizzle.error( match[0] );
- }
-
- match[2] = match[2].replace(/^\+|\s*/g, '');
-
- // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
- var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
- match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
- !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
-
- // calculate the numbers (first)n+(last) including if they are negative
- match[2] = (test[1] + (test[2] || 1)) - 0;
- match[3] = test[3] - 0;
- }
- else if ( match[2] ) {
- Sizzle.error( match[0] );
- }
-
- // TODO: Move to normal caching system
- match[0] = done++;
-
- return match;
- },
-
- ATTR: function( match, curLoop, inplace, result, not, isXML ) {
- var name = match[1] = match[1].replace( rBackslash, "" );
-
- if ( !isXML && Expr.attrMap[name] ) {
- match[1] = Expr.attrMap[name];
- }
-
- // Handle if an un-quoted value was used
- match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
-
- if ( match[2] === "~=" ) {
- match[4] = " " + match[4] + " ";
- }
-
- return match;
- },
-
- PSEUDO: function( match, curLoop, inplace, result, not ) {
- if ( match[1] === "not" ) {
- // If we're dealing with a complex expression, or a simple one
- if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
- match[3] = Sizzle(match[3], null, null, curLoop);
-
- } else {
- var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
-
- if ( !inplace ) {
- result.push.apply( result, ret );
- }
-
- return false;
- }
-
- } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
- return true;
- }
-
- return match;
- },
-
- POS: function( match ) {
- match.unshift( true );
-
- return match;
- }
- },
-
- filters: {
- enabled: function( elem ) {
- return elem.disabled === false && elem.type !== "hidden";
- },
-
- disabled: function( elem ) {
- return elem.disabled === true;
- },
-
- checked: function( elem ) {
- return elem.checked === true;
- },
-
- selected: function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- parent: function( elem ) {
- return !!elem.firstChild;
- },
-
- empty: function( elem ) {
- return !elem.firstChild;
- },
-
- has: function( elem, i, match ) {
- return !!Sizzle( match[3], elem ).length;
- },
-
- header: function( elem ) {
- return (/h\d/i).test( elem.nodeName );
- },
-
- text: function( elem ) {
- var attr = elem.getAttribute( "type" ), type = elem.type;
- // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
- // use getAttribute instead to test this case
- return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
- },
-
- radio: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
- },
-
- checkbox: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
- },
-
- file: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
- },
-
- password: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
- },
-
- submit: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && "submit" === elem.type;
- },
-
- image: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
- },
-
- reset: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && "reset" === elem.type;
- },
-
- button: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && "button" === elem.type || name === "button";
- },
-
- input: function( elem ) {
- return (/input|select|textarea|button/i).test( elem.nodeName );
- },
-
- focus: function( elem ) {
- return elem === elem.ownerDocument.activeElement;
- }
- },
- setFilters: {
- first: function( elem, i ) {
- return i === 0;
- },
-
- last: function( elem, i, match, array ) {
- return i === array.length - 1;
- },
-
- even: function( elem, i ) {
- return i % 2 === 0;
- },
-
- odd: function( elem, i ) {
- return i % 2 === 1;
- },
-
- lt: function( elem, i, match ) {
- return i < match[3] - 0;
- },
-
- gt: function( elem, i, match ) {
- return i > match[3] - 0;
- },
-
- nth: function( elem, i, match ) {
- return match[3] - 0 === i;
- },
-
- eq: function( elem, i, match ) {
- return match[3] - 0 === i;
- }
- },
- filter: {
- PSEUDO: function( elem, match, i, array ) {
- var name = match[1],
- filter = Expr.filters[ name ];
-
- if ( filter ) {
- return filter( elem, i, match, array );
-
- } else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
-
- } else if ( name === "not" ) {
- var not = match[3];
-
- for ( var j = 0, l = not.length; j < l; j++ ) {
- if ( not[j] === elem ) {
- return false;
- }
- }
-
- return true;
-
- } else {
- Sizzle.error( name );
- }
- },
-
- CHILD: function( elem, match ) {
- var type = match[1],
- node = elem;
-
- switch ( type ) {
- case "only":
- case "first":
- while ( (node = node.previousSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- if ( type === "first" ) {
- return true;
- }
-
- node = elem;
-
- case "last":
- while ( (node = node.nextSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- return true;
-
- case "nth":
- var first = match[2],
- last = match[3];
-
- if ( first === 1 && last === 0 ) {
- return true;
- }
-
- var doneName = match[0],
- parent = elem.parentNode;
-
- if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
- var count = 0;
-
- for ( node = parent.firstChild; node; node = node.nextSibling ) {
- if ( node.nodeType === 1 ) {
- node.nodeIndex = ++count;
- }
- }
-
- parent.sizcache = doneName;
- }
-
- var diff = elem.nodeIndex - last;
-
- if ( first === 0 ) {
- return diff === 0;
-
- } else {
- return ( diff % first === 0 && diff / first >= 0 );
- }
- }
- },
-
- ID: function( elem, match ) {
- return elem.nodeType === 1 && elem.getAttribute("id") === match;
- },
-
- TAG: function( elem, match ) {
- return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
- },
-
- CLASS: function( elem, match ) {
- return (" " + (elem.className || elem.getAttribute("class")) + " ")
- .indexOf( match ) > -1;
- },
-
- ATTR: function( elem, match ) {
- var name = match[1],
- result = Expr.attrHandle[ name ] ?
- Expr.attrHandle[ name ]( elem ) :
- elem[ name ] != null ?
- elem[ name ] :
- elem.getAttribute( name ),
- value = result + "",
- type = match[2],
- check = match[4];
-
- return result == null ?
- type === "!=" :
- type === "=" ?
- value === check :
- type === "*=" ?
- value.indexOf(check) >= 0 :
- type === "~=" ?
- (" " + value + " ").indexOf(check) >= 0 :
- !check ?
- value && result !== false :
- type === "!=" ?
- value !== check :
- type === "^=" ?
- value.indexOf(check) === 0 :
- type === "$=" ?
- value.substr(value.length - check.length) === check :
- type === "|=" ?
- value === check || value.substr(0, check.length + 1) === check + "-" :
- false;
- },
-
- POS: function( elem, match, i, array ) {
- var name = match[2],
- filter = Expr.setFilters[ name ];
-
- if ( filter ) {
- return filter( elem, i, match, array );
- }
- }
- }
-};
-
-var origPOS = Expr.match.POS,
- fescape = function(all, num){
- return "\\" + (num - 0 + 1);
- };
-
-for ( var type in Expr.match ) {
- Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
- Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
-}
-
-var makeArray = function( array, results ) {
- array = Array.prototype.slice.call( array, 0 );
-
- if ( results ) {
- results.push.apply( results, array );
- return results;
- }
-
- return array;
-};
-
-// Perform a simple check to determine if the browser is capable of
-// converting a NodeList to an array using builtin methods.
-// Also verifies that the returned array holds DOM nodes
-// (which is not the case in the Blackberry browser)
-try {
- Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
-
-// Provide a fallback method if it does not work
-} catch( e ) {
- makeArray = function( array, results ) {
- var i = 0,
- ret = results || [];
-
- if ( toString.call(array) === "[object Array]" ) {
- Array.prototype.push.apply( ret, array );
-
- } else {
- if ( typeof array.length === "number" ) {
- for ( var l = array.length; i < l; i++ ) {
- ret.push( array[i] );
- }
-
- } else {
- for ( ; array[i]; i++ ) {
- ret.push( array[i] );
- }
- }
- }
-
- return ret;
- };
-}
-
-var sortOrder, siblingCheck;
-
-if ( document.documentElement.compareDocumentPosition ) {
- sortOrder = function( a, b ) {
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
- return a.compareDocumentPosition ? -1 : 1;
- }
-
- return a.compareDocumentPosition(b) & 4 ? -1 : 1;
- };
-
-} else {
- sortOrder = function( a, b ) {
- // The nodes are identical, we can exit early
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
-
- // Fallback to using sourceIndex (in IE) if it's available on both nodes
- } else if ( a.sourceIndex && b.sourceIndex ) {
- return a.sourceIndex - b.sourceIndex;
- }
-
- var al, bl,
- ap = [],
- bp = [],
- aup = a.parentNode,
- bup = b.parentNode,
- cur = aup;
-
- // If the nodes are siblings (or identical) we can do a quick check
- if ( aup === bup ) {
- return siblingCheck( a, b );
-
- // If no parents were found then the nodes are disconnected
- } else if ( !aup ) {
- return -1;
-
- } else if ( !bup ) {
- return 1;
- }
-
- // Otherwise they're somewhere else in the tree so we need
- // to build up a full list of the parentNodes for comparison
- while ( cur ) {
- ap.unshift( cur );
- cur = cur.parentNode;
- }
-
- cur = bup;
-
- while ( cur ) {
- bp.unshift( cur );
- cur = cur.parentNode;
- }
-
- al = ap.length;
- bl = bp.length;
-
- // Start walking down the tree looking for a discrepancy
- for ( var i = 0; i < al && i < bl; i++ ) {
- if ( ap[i] !== bp[i] ) {
- return siblingCheck( ap[i], bp[i] );
- }
- }
-
- // We ended someplace up the tree so do a sibling check
- return i === al ?
- siblingCheck( a, bp[i], -1 ) :
- siblingCheck( ap[i], b, 1 );
- };
-
- siblingCheck = function( a, b, ret ) {
- if ( a === b ) {
- return ret;
- }
-
- var cur = a.nextSibling;
-
- while ( cur ) {
- if ( cur === b ) {
- return -1;
- }
-
- cur = cur.nextSibling;
- }
-
- return 1;
- };
-}
-
-// Utility function for retreiving the text value of an array of DOM nodes
-Sizzle.getText = function( elems ) {
- var ret = "", elem;
-
- for ( var i = 0; elems[i]; i++ ) {
- elem = elems[i];
-
- // Get the text from text nodes and CDATA nodes
- if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
- ret += elem.nodeValue;
-
- // Traverse everything else, except comment nodes
- } else if ( elem.nodeType !== 8 ) {
- ret += Sizzle.getText( elem.childNodes );
- }
- }
-
- return ret;
-};
-
-// Check to see if the browser returns elements by name when
-// querying by getElementById (and provide a workaround)
-(function(){
- // We're going to inject a fake input element with a specified name
- var form = document.createElement("div"),
- id = "script" + (new Date()).getTime(),
- root = document.documentElement;
-
- form.innerHTML = "<a name='" + id + "'/>";
-
- // Inject it into the root element, check its status, and remove it quickly
- root.insertBefore( form, root.firstChild );
-
- // The workaround has to do additional checks after a getElementById
- // Which slows things down for other browsers (hence the branching)
- if ( document.getElementById( id ) ) {
- Expr.find.ID = function( match, context, isXML ) {
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
-
- return m ?
- m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
- [m] :
- undefined :
- [];
- }
- };
-
- Expr.filter.ID = function( elem, match ) {
- var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
-
- return elem.nodeType === 1 && node && node.nodeValue === match;
- };
- }
-
- root.removeChild( form );
-
- // release memory in IE
- root = form = null;
-})();
-
-(function(){
- // Check to see if the browser returns only elements
- // when doing getElementsByTagName("*")
-
- // Create a fake element
- var div = document.createElement("div");
- div.appendChild( document.createComment("") );
-
- // Make sure no comments are found
- if ( div.getElementsByTagName("*").length > 0 ) {
- Expr.find.TAG = function( match, context ) {
- var results = context.getElementsByTagName( match[1] );
-
- // Filter out possible comments
- if ( match[1] === "*" ) {
- var tmp = [];
-
- for ( var i = 0; results[i]; i++ ) {
- if ( results[i].nodeType === 1 ) {
- tmp.push( results[i] );
- }
- }
-
- results = tmp;
- }
-
- return results;
- };
- }
-
- // Check to see if an attribute returns normalized href attributes
- div.innerHTML = "<a href='#'></a>";
-
- if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
- div.firstChild.getAttribute("href") !== "#" ) {
-
- Expr.attrHandle.href = function( elem ) {
- return elem.getAttribute( "href", 2 );
- };
- }
-
- // release memory in IE
- div = null;
-})();
-
-if ( document.querySelectorAll ) {
- (function(){
- var oldSizzle = Sizzle,
- div = document.createElement("div"),
- id = "__sizzle__";
-
- div.innerHTML = "<p class='TEST'></p>";
-
- // Safari can't handle uppercase or unicode characters when
- // in quirks mode.
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
- return;
- }
-
- Sizzle = function( query, context, extra, seed ) {
- context = context || document;
-
- // Only use querySelectorAll on non-XML documents
- // (ID selectors don't work in non-HTML documents)
- if ( !seed && !Sizzle.isXML(context) ) {
- // See if we find a selector to speed up
- var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
-
- if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
- // Speed-up: Sizzle("TAG")
- if ( match[1] ) {
- return makeArray( context.getElementsByTagName( query ), extra );
-
- // Speed-up: Sizzle(".CLASS")
- } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
- return makeArray( context.getElementsByClassName( match[2] ), extra );
- }
- }
-
- if ( context.nodeType === 9 ) {
- // Speed-up: Sizzle("body")
- // The body element only exists once, optimize finding it
- if ( query === "body" && context.body ) {
- return makeArray( [ context.body ], extra );
-
- // Speed-up: Sizzle("#ID")
- } else if ( match && match[3] ) {
- var elem = context.getElementById( match[3] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id === match[3] ) {
- return makeArray( [ elem ], extra );
- }
-
- } else {
- return makeArray( [], extra );
- }
- }
-
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(qsaError) {}
-
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- var oldContext = context,
- old = context.getAttribute( "id" ),
- nid = old || id,
- hasParent = context.parentNode,
- relativeHierarchySelector = /^\s*[+~]/.test( query );
-
- if ( !old ) {
- context.setAttribute( "id", nid );
- } else {
- nid = nid.replace( /'/g, "\\$&" );
- }
- if ( relativeHierarchySelector && hasParent ) {
- context = context.parentNode;
- }
-
- try {
- if ( !relativeHierarchySelector || hasParent ) {
- return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
- }
-
- } catch(pseudoError) {
- } finally {
- if ( !old ) {
- oldContext.removeAttribute( "id" );
- }
- }
- }
- }
-
- return oldSizzle(query, context, extra, seed);
- };
-
- for ( var prop in oldSizzle ) {
- Sizzle[ prop ] = oldSizzle[ prop ];
- }
-
- // release memory in IE
- div = null;
- })();
-}
-
-(function(){
- var html = document.documentElement,
- matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
-
- if ( matches ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9 fails this)
- var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
- pseudoWorks = false;
-
- try {
- // This should fail with an exception
- // Gecko does not error, returns false instead
- matches.call( document.documentElement, "[test!='']:sizzle" );
-
- } catch( pseudoError ) {
- pseudoWorks = true;
- }
-
- Sizzle.matchesSelector = function( node, expr ) {
- // Make sure that attribute selectors are quoted
- expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
-
- if ( !Sizzle.isXML( node ) ) {
- try {
- if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
- var ret = matches.call( node, expr );
-
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || !disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9, so check for that
- node.document && node.document.nodeType !== 11 ) {
- return ret;
- }
- }
- } catch(e) {}
- }
-
- return Sizzle(expr, null, null, [node]).length > 0;
- };
- }
-})();
-
-(function(){
- var div = document.createElement("div");
-
- div.innerHTML = "<div class='test e'></div><div class='test'></div>";
-
- // Opera can't find a second classname (in 9.6)
- // Also, make sure that getElementsByClassName actually exists
- if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
- return;
- }
-
- // Safari caches class attributes, doesn't catch changes (in 3.2)
- div.lastChild.className = "e";
-
- if ( div.getElementsByClassName("e").length === 1 ) {
- return;
- }
-
- Expr.order.splice(1, 0, "CLASS");
- Expr.find.CLASS = function( match, context, isXML ) {
- if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
- return context.getElementsByClassName(match[1]);
- }
- };
-
- // release memory in IE
- div = null;
-})();
-
-function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
-
- if ( elem ) {
- var match = false;
-
- elem = elem[dir];
-
- while ( elem ) {
- if ( elem.sizcache === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
-
- if ( elem.nodeType === 1 && !isXML ){
- elem.sizcache = doneName;
- elem.sizset = i;
- }
-
- if ( elem.nodeName.toLowerCase() === cur ) {
- match = elem;
- break;
- }
-
- elem = elem[dir];
- }
-
- checkSet[i] = match;
- }
- }
-}
-
-function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
-
- if ( elem ) {
- var match = false;
-
- elem = elem[dir];
-
- while ( elem ) {
- if ( elem.sizcache === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
-
- if ( elem.nodeType === 1 ) {
- if ( !isXML ) {
- elem.sizcache = doneName;
- elem.sizset = i;
- }
-
- if ( typeof cur !== "string" ) {
- if ( elem === cur ) {
- match = true;
- break;
- }
-
- } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
- match = elem;
- break;
- }
- }
-
- elem = elem[dir];
- }
-
- checkSet[i] = match;
- }
- }
-}
-
-if ( document.documentElement.contains ) {
- Sizzle.contains = function( a, b ) {
- return a !== b && (a.contains ? a.contains(b) : true);
- };
-
-} else if ( document.documentElement.compareDocumentPosition ) {
- Sizzle.contains = function( a, b ) {
- return !!(a.compareDocumentPosition(b) & 16);
- };
-
-} else {
- Sizzle.contains = function() {
- return false;
- };
-}
-
-Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
-
- return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-var posProcess = function( selector, context ) {
- var match,
- tmpSet = [],
- later = "",
- root = context.nodeType ? [context] : context;
-
- // Position selectors must be done after the filter
- // And so must :not(positional) so we move all PSEUDOs to the end
- while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
- later += match[0];
- selector = selector.replace( Expr.match.PSEUDO, "" );
- }
-
- selector = Expr.relative[selector] ? selector + "*" : selector;
-
- for ( var i = 0, l = root.length; i < l; i++ ) {
- Sizzle( selector, root[i], tmpSet );
- }
-
- return Sizzle.filter( later, tmpSet );
-};
-
-// EXPOSE
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.filters;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-})();
-
-
-var runtil = /Until$/,
- rparentsprev = /^(?:parents|prevUntil|prevAll)/,
- // Note: This RegExp should be improved, or likely pulled from Sizzle
- rmultiselector = /,/,
- isSimple = /^.[^:#\[\.,]*$/,
- slice = Array.prototype.slice,
- POS = jQuery.expr.match.POS,
- // methods guaranteed to produce a unique set when starting from a unique set
- guaranteedUnique = {
- children: true,
- contents: true,
- next: true,
- prev: true
- };
-
-jQuery.fn.extend({
- find: function( selector ) {
- var self = this,
- i, l;
-
- if ( typeof selector !== "string" ) {
- return jQuery( selector ).filter(function() {
- for ( i = 0, l = self.length; i < l; i++ ) {
- if ( jQuery.contains( self[ i ], this ) ) {
- return true;
- }
- }
- });
- }
-
- var ret = this.pushStack( "", "find", selector ),
- length, n, r;
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- length = ret.length;
- jQuery.find( selector, this[i], ret );
-
- if ( i > 0 ) {
- // Make sure that the results are unique
- for ( n = length; n < ret.length; n++ ) {
- for ( r = 0; r < length; r++ ) {
- if ( ret[r] === ret[n] ) {
- ret.splice(n--, 1);
- break;
- }
- }
- }
- }
- }
-
- return ret;
- },
-
- has: function( target ) {
- var targets = jQuery( target );
- return this.filter(function() {
- for ( var i = 0, l = targets.length; i < l; i++ ) {
- if ( jQuery.contains( this, targets[i] ) ) {
- return true;
- }
- }
- });
- },
-
- not: function( selector ) {
- return this.pushStack( winnow(this, selector, false), "not", selector);
- },
-
- filter: function( selector ) {
- return this.pushStack( winnow(this, selector, true), "filter", selector );
- },
-
- is: function( selector ) {
- return !!selector && ( typeof selector === "string" ?
- jQuery.filter( selector, this ).length > 0 :
- this.filter( selector ).length > 0 );
- },
-
- closest: function( selectors, context ) {
- var ret = [], i, l, cur = this[0];
-
- // Array
- if ( jQuery.isArray( selectors ) ) {
- var match, selector,
- matches = {},
- level = 1;
-
- if ( cur && selectors.length ) {
- for ( i = 0, l = selectors.length; i < l; i++ ) {
- selector = selectors[i];
-
- if ( !matches[ selector ] ) {
- matches[ selector ] = POS.test( selector ) ?
- jQuery( selector, context || this.context ) :
- selector;
- }
- }
-
- while ( cur && cur.ownerDocument && cur !== context ) {
- for ( selector in matches ) {
- match = matches[ selector ];
-
- if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
- ret.push({ selector: selector, elem: cur, level: level });
- }
- }
-
- cur = cur.parentNode;
- level++;
- }
- }
-
- return ret;
- }
-
- // String
- var pos = POS.test( selectors ) || typeof selectors !== "string" ?
- jQuery( selectors, context || this.context ) :
- 0;
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- cur = this[i];
-
- while ( cur ) {
- if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- ret.push( cur );
- break;
-
- } else {
- cur = cur.parentNode;
- if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
- break;
- }
- }
- }
- }
-
- ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
-
- return this.pushStack( ret, "closest", selectors );
- },
-
- // Determine the position of an element within
- // the matched set of elements
- index: function( elem ) {
- if ( !elem || typeof elem === "string" ) {
- return jQuery.inArray( this[0],
- // If it receives a string, the selector is used
- // If it receives nothing, the siblings are used
- elem ? jQuery( elem ) : this.parent().children() );
- }
- // Locate the position of the desired element
- return jQuery.inArray(
- // If it receives a jQuery object, the first element is used
- elem.jquery ? elem[0] : elem, this );
- },
-
- add: function( selector, context ) {
- var set = typeof selector === "string" ?
- jQuery( selector, context ) :
- jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
- all = jQuery.merge( this.get(), set );
-
- return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
- all :
- jQuery.unique( all ) );
- },
-
- andSelf: function() {
- return this.add( this.prevObject );
- }
-});
-
-// A painfully simple check to see if an element is disconnected
-// from a document (should be improved, where feasible).
-function isDisconnected( node ) {
- return !node || !node.parentNode || node.parentNode.nodeType === 11;
-}
-
-jQuery.each({
- parent: function( elem ) {
- var parent = elem.parentNode;
- return parent && parent.nodeType !== 11 ? parent : null;
- },
- parents: function( elem ) {
- return jQuery.dir( elem, "parentNode" );
- },
- parentsUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "parentNode", until );
- },
- next: function( elem ) {
- return jQuery.nth( elem, 2, "nextSibling" );
- },
- prev: function( elem ) {
- return jQuery.nth( elem, 2, "previousSibling" );
- },
- nextAll: function( elem ) {
- return jQuery.dir( elem, "nextSibling" );
- },
- prevAll: function( elem ) {
- return jQuery.dir( elem, "previousSibling" );
- },
- nextUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "nextSibling", until );
- },
- prevUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "previousSibling", until );
- },
- siblings: function( elem ) {
- return jQuery.sibling( elem.parentNode.firstChild, elem );
- },
- children: function( elem ) {
- return jQuery.sibling( elem.firstChild );
- },
- contents: function( elem ) {
- return jQuery.nodeName( elem, "iframe" ) ?
- elem.contentDocument || elem.contentWindow.document :
- jQuery.makeArray( elem.childNodes );
- }
-}, function( name, fn ) {
- jQuery.fn[ name ] = function( until, selector ) {
- var ret = jQuery.map( this, fn, until ),
- // The variable 'args' was introduced in
- // https://github.com/jquery/jquery/commit/52a0238
- // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
- // http://code.google.com/p/v8/issues/detail?id=1050
- args = slice.call(arguments);
-
- if ( !runtil.test( name ) ) {
- selector = until;
- }
-
- if ( selector && typeof selector === "string" ) {
- ret = jQuery.filter( selector, ret );
- }
-
- ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
-
- if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
- ret = ret.reverse();
- }
-
- return this.pushStack( ret, name, args.join(",") );
- };
-});
-
-jQuery.extend({
- filter: function( expr, elems, not ) {
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
-
- return elems.length === 1 ?
- jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
- jQuery.find.matches(expr, elems);
- },
-
- dir: function( elem, dir, until ) {
- var matched = [],
- cur = elem[ dir ];
-
- while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
- if ( cur.nodeType === 1 ) {
- matched.push( cur );
- }
- cur = cur[dir];
- }
- return matched;
- },
-
- nth: function( cur, result, dir, elem ) {
- result = result || 1;
- var num = 0;
-
- for ( ; cur; cur = cur[dir] ) {
- if ( cur.nodeType === 1 && ++num === result ) {
- break;
- }
- }
-
- return cur;
- },
-
- sibling: function( n, elem ) {
- var r = [];
-
- for ( ; n; n = n.nextSibling ) {
- if ( n.nodeType === 1 && n !== elem ) {
- r.push( n );
- }
- }
-
- return r;
- }
-});
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, keep ) {
-
- // Can't pass null or undefined to indexOf in Firefox 4
- // Set to 0 to skip string check
- qualifier = qualifier || 0;
-
- if ( jQuery.isFunction( qualifier ) ) {
- return jQuery.grep(elements, function( elem, i ) {
- var retVal = !!qualifier.call( elem, i, elem );
- return retVal === keep;
- });
-
- } else if ( qualifier.nodeType ) {
- return jQuery.grep(elements, function( elem, i ) {
- return (elem === qualifier) === keep;
- });
-
- } else if ( typeof qualifier === "string" ) {
- var filtered = jQuery.grep(elements, function( elem ) {
- return elem.nodeType === 1;
- });
-
- if ( isSimple.test( qualifier ) ) {
- return jQuery.filter(qualifier, filtered, !keep);
- } else {
- qualifier = jQuery.filter( qualifier, filtered );
- }
- }
-
- return jQuery.grep(elements, function( elem, i ) {
- return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
- });
-}
-
-
-
-
-var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
- rleadingWhitespace = /^\s+/,
- rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
- rtagName = /<([\w:]+)/,
- rtbody = /<tbody/i,
- rhtml = /<|&#?\w+;/,
- rnocache = /<(?:script|object|embed|option|style)/i,
- // checked="checked" or checked
- rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
- rscriptType = /\/(java|ecma)script/i,
- rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
- wrapMap = {
- option: [ 1, "<select multiple='multiple'>", "</select>" ],
- legend: [ 1, "<fieldset>", "</fieldset>" ],
- thead: [ 1, "<table>", "</table>" ],
- tr: [ 2, "<table><tbody>", "</tbody></table>" ],
- td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
- col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
- area: [ 1, "<map>", "</map>" ],
- _default: [ 0, "", "" ]
- };
-
-wrapMap.optgroup = wrapMap.option;
-wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
-wrapMap.th = wrapMap.td;
-
-// IE can't serialize <link> and <script> tags normally
-if ( !jQuery.support.htmlSerialize ) {
- wrapMap._default = [ 1, "div<div>", "</div>" ];
-}
-
-jQuery.fn.extend({
- text: function( text ) {
- if ( jQuery.isFunction(text) ) {
- return this.each(function(i) {
- var self = jQuery( this );
-
- self.text( text.call(this, i, self.text()) );
- });
- }
-
- if ( typeof text !== "object" && text !== undefined ) {
- return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
- }
-
- return jQuery.text( this );
- },
-
- wrapAll: function( html ) {
- if ( jQuery.isFunction( html ) ) {
- return this.each(function(i) {
- jQuery(this).wrapAll( html.call(this, i) );
- });
- }
-
- if ( this[0] ) {
- // The elements to wrap the target around
- var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
-
- if ( this[0].parentNode ) {
- wrap.insertBefore( this[0] );
- }
-
- wrap.map(function() {
- var elem = this;
-
- while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
- elem = elem.firstChild;
- }
-
- return elem;
- }).append( this );
- }
-
- return this;
- },
-
- wrapInner: function( html ) {
- if ( jQuery.isFunction( html ) ) {
- return this.each(function(i) {
- jQuery(this).wrapInner( html.call(this, i) );
- });
- }
-
- return this.each(function() {
- var self = jQuery( this ),
- contents = self.contents();
-
- if ( contents.length ) {
- contents.wrapAll( html );
-
- } else {
- self.append( html );
- }
- });
- },
-
- wrap: function( html ) {
- return this.each(function() {
- jQuery( this ).wrapAll( html );
- });
- },
-
- unwrap: function() {
- return this.parent().each(function() {
- if ( !jQuery.nodeName( this, "body" ) ) {
- jQuery( this ).replaceWith( this.childNodes );
- }
- }).end();
- },
-
- append: function() {
- return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 ) {
- this.appendChild( elem );
- }
- });
- },
-
- prepend: function() {
- return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 ) {
- this.insertBefore( elem, this.firstChild );
- }
- });
- },
-
- before: function() {
- if ( this[0] && this[0].parentNode ) {
- return this.domManip(arguments, false, function( elem ) {
- this.parentNode.insertBefore( elem, this );
- });
- } else if ( arguments.length ) {
- var set = jQuery(arguments[0]);
- set.push.apply( set, this.toArray() );
- return this.pushStack( set, "before", arguments );
- }
- },
-
- after: function() {
- if ( this[0] && this[0].parentNode ) {
- return this.domManip(arguments, false, function( elem ) {
- this.parentNode.insertBefore( elem, this.nextSibling );
- });
- } else if ( arguments.length ) {
- var set = this.pushStack( this, "after", arguments );
- set.push.apply( set, jQuery(arguments[0]).toArray() );
- return set;
- }
- },
-
- // keepData is for internal use only--do not document
- remove: function( selector, keepData ) {
- for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
- if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
- if ( !keepData && elem.nodeType === 1 ) {
- jQuery.cleanData( elem.getElementsByTagName("*") );
- jQuery.cleanData( [ elem ] );
- }
-
- if ( elem.parentNode ) {
- elem.parentNode.removeChild( elem );
- }
- }
- }
-
- return this;
- },
-
- empty: function() {
- for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
- // Remove element nodes and prevent memory leaks
- if ( elem.nodeType === 1 ) {
- jQuery.cleanData( elem.getElementsByTagName("*") );
- }
-
- // Remove any remaining nodes
- while ( elem.firstChild ) {
- elem.removeChild( elem.firstChild );
- }
- }
-
- return this;
- },
-
- clone: function( dataAndEvents, deepDataAndEvents ) {
- dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
- deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
-
- return this.map( function () {
- return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
- });
- },
-
- html: function( value ) {
- if ( value === undefined ) {
- return this[0] && this[0].nodeType === 1 ?
- this[0].innerHTML.replace(rinlinejQuery, "") :
- null;
-
- // See if we can take a shortcut and just use innerHTML
- } else if ( typeof value === "string" && !rnocache.test( value ) &&
- (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
- !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
-
- value = value.replace(rxhtmlTag, "<$1></$2>");
-
- try {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- // Remove element nodes and prevent memory leaks
- if ( this[i].nodeType === 1 ) {
- jQuery.cleanData( this[i].getElementsByTagName("*") );
- this[i].innerHTML = value;
- }
- }
-
- // If using innerHTML throws an exception, use the fallback method
- } catch(e) {
- this.empty().append( value );
- }
-
- } else if ( jQuery.isFunction( value ) ) {
- this.each(function(i){
- var self = jQuery( this );
-
- self.html( value.call(this, i, self.html()) );
- });
-
- } else {
- this.empty().append( value );
- }
-
- return this;
- },
-
- replaceWith: function( value ) {
- if ( this[0] && this[0].parentNode ) {
- // Make sure that the elements are removed from the DOM before they are inserted
- // this can help fix replacing a parent with child elements
- if ( jQuery.isFunction( value ) ) {
- return this.each(function(i) {
- var self = jQuery(this), old = self.html();
- self.replaceWith( value.call( this, i, old ) );
- });
- }
-
- if ( typeof value !== "string" ) {
- value = jQuery( value ).detach();
- }
-
- return this.each(function() {
- var next = this.nextSibling,
- parent = this.parentNode;
-
- jQuery( this ).remove();
-
- if ( next ) {
- jQuery(next).before( value );
- } else {
- jQuery(parent).append( value );
- }
- });
- } else {
- return this.length ?
- this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
- this;
- }
- },
-
- detach: function( selector ) {
- return this.remove( selector, true );
- },
-
- domManip: function( args, table, callback ) {
- var results, first, fragment, parent,
- value = args[0],
- scripts = [];
-
- // We can't cloneNode fragments that contain checked, in WebKit
- if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
- return this.each(function() {
- jQuery(this).domManip( args, table, callback, true );
- });
- }
-
- if ( jQuery.isFunction(value) ) {
- return this.each(function(i) {
- var self = jQuery(this);
- args[0] = value.call(this, i, table ? self.html() : undefined);
- self.domManip( args, table, callback );
- });
- }
-
- if ( this[0] ) {
- parent = value && value.parentNode;
-
- // If we're in a fragment, just use that instead of building a new one
- if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
- results = { fragment: parent };
-
- } else {
- results = jQuery.buildFragment( args, this, scripts );
- }
-
- fragment = results.fragment;
-
- if ( fragment.childNodes.length === 1 ) {
- first = fragment = fragment.firstChild;
- } else {
- first = fragment.firstChild;
- }
-
- if ( first ) {
- table = table && jQuery.nodeName( first, "tr" );
-
- for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
- callback.call(
- table ?
- root(this[i], first) :
- this[i],
- // Make sure that we do not leak memory by inadvertently discarding
- // the original fragment (which might have attached data) instead of
- // using it; in addition, use the original fragment object for the last
- // item instead of first because it can end up being emptied incorrectly
- // in certain situations (Bug #8070).
- // Fragments from the fragment cache must always be cloned and never used
- // in place.
- results.cacheable || (l > 1 && i < lastIndex) ?
- jQuery.clone( fragment, true, true ) :
- fragment
- );
- }
- }
-
- if ( scripts.length ) {
- jQuery.each( scripts, evalScript );
- }
- }
-
- return this;
- }
-});
-
-function root( elem, cur ) {
- return jQuery.nodeName(elem, "table") ?
- (elem.getElementsByTagName("tbody")[0] ||
- elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
- elem;
-}
-
-function cloneCopyEvent( src, dest ) {
-
- if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
- return;
- }
-
- var internalKey = jQuery.expando,
- oldData = jQuery.data( src ),
- curData = jQuery.data( dest, oldData );
-
- // Switch to use the internal data object, if it exists, for the next
- // stage of data copying
- if ( (oldData = oldData[ internalKey ]) ) {
- var events = oldData.events;
- curData = curData[ internalKey ] = jQuery.extend({}, oldData);
-
- if ( events ) {
- delete curData.handle;
- curData.events = {};
-
- for ( var type in events ) {
- for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
- jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
- }
- }
- }
- }
-}
-
-function cloneFixAttributes( src, dest ) {
- var nodeName;
-
- // We do not need to do anything for non-Elements
- if ( dest.nodeType !== 1 ) {
- return;
- }
-
- // clearAttributes removes the attributes, which we don't want,
- // but also removes the attachEvent events, which we *do* want
- if ( dest.clearAttributes ) {
- dest.clearAttributes();
- }
-
- // mergeAttributes, in contrast, only merges back on the
- // original attributes, not the events
- if ( dest.mergeAttributes ) {
- dest.mergeAttributes( src );
- }
-
- nodeName = dest.nodeName.toLowerCase();
-
- // IE6-8 fail to clone children inside object elements that use
- // the proprietary classid attribute value (rather than the type
- // attribute) to identify the type of content to display
- if ( nodeName === "object" ) {
- dest.outerHTML = src.outerHTML;
-
- } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
- // IE6-8 fails to persist the checked state of a cloned checkbox
- // or radio button. Worse, IE6-7 fail to give the cloned element
- // a checked appearance if the defaultChecked value isn't also set
- if ( src.checked ) {
- dest.defaultChecked = dest.checked = src.checked;
- }
-
- // IE6-7 get confused and end up setting the value of a cloned
- // checkbox/radio button to an empty string instead of "on"
- if ( dest.value !== src.value ) {
- dest.value = src.value;
- }
-
- // IE6-8 fails to return the selected option to the default selected
- // state when cloning options
- } else if ( nodeName === "option" ) {
- dest.selected = src.defaultSelected;
-
- // IE6-8 fails to set the defaultValue to the correct value when
- // cloning other types of input fields
- } else if ( nodeName === "input" || nodeName === "textarea" ) {
- dest.defaultValue = src.defaultValue;
- }
-
- // Event data gets referenced instead of copied if the expando
- // gets copied too
- dest.removeAttribute( jQuery.expando );
-}
-
-jQuery.buildFragment = function( args, nodes, scripts ) {
- var fragment, cacheable, cacheresults, doc;
-
- // nodes may contain either an explicit document object,
- // a jQuery collection or context object.
- // If nodes[0] contains a valid object to assign to doc
- if ( nodes && nodes[0] ) {
- doc = nodes[0].ownerDocument || nodes[0];
- }
-
- // Ensure that an attr object doesn't incorrectly stand in as a document object
- // Chrome and Firefox seem to allow this to occur and will throw exception
- // Fixes #8950
- if ( !doc.createDocumentFragment ) {
- doc = document;
- }
-
- // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
- // Cloning options loses the selected state, so don't cache them
- // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
- // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
- if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
- args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
-
- cacheable = true;
-
- cacheresults = jQuery.fragments[ args[0] ];
- if ( cacheresults && cacheresults !== 1 ) {
- fragment = cacheresults;
- }
- }
-
- if ( !fragment ) {
- fragment = doc.createDocumentFragment();
- jQuery.clean( args, doc, fragment, scripts );
- }
-
- if ( cacheable ) {
- jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
- }
-
- return { fragment: fragment, cacheable: cacheable };
-};
-
-jQuery.fragments = {};
-
-jQuery.each({
- appendTo: "append",
- prependTo: "prepend",
- insertBefore: "before",
- insertAfter: "after",
- replaceAll: "replaceWith"
-}, function( name, original ) {
- jQuery.fn[ name ] = function( selector ) {
- var ret = [],
- insert = jQuery( selector ),
- parent = this.length === 1 && this[0].parentNode;
-
- if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
- insert[ original ]( this[0] );
- return this;
-
- } else {
- for ( var i = 0, l = insert.length; i < l; i++ ) {
- var elems = (i > 0 ? this.clone(true) : this).get();
- jQuery( insert[i] )[ original ]( elems );
- ret = ret.concat( elems );
- }
-
- return this.pushStack( ret, name, insert.selector );
- }
- };
-});
-
-function getAll( elem ) {
- if ( "getElementsByTagName" in elem ) {
- return elem.getElementsByTagName( "*" );
-
- } else if ( "querySelectorAll" in elem ) {
- return elem.querySelectorAll( "*" );
-
- } else {
- return [];
- }
-}
-
-// Used in clean, fixes the defaultChecked property
-function fixDefaultChecked( elem ) {
- if ( elem.type === "checkbox" || elem.type === "radio" ) {
- elem.defaultChecked = elem.checked;
- }
-}
-// Finds all inputs and passes them to fixDefaultChecked
-function findInputs( elem ) {
- if ( jQuery.nodeName( elem, "input" ) ) {
- fixDefaultChecked( elem );
- } else if ( "getElementsByTagName" in elem ) {
- jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
- }
-}
-
-jQuery.extend({
- clone: function( elem, dataAndEvents, deepDataAndEvents ) {
- var clone = elem.cloneNode(true),
- srcElements,
- destElements,
- i;
-
- if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
- (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
- // IE copies events bound via attachEvent when using cloneNode.
- // Calling detachEvent on the clone will also remove the events
- // from the original. In order to get around this, we use some
- // proprietary methods to clear the events. Thanks to MooTools
- // guys for this hotness.
-
- cloneFixAttributes( elem, clone );
-
- // Using Sizzle here is crazy slow, so we use getElementsByTagName
- // instead
- srcElements = getAll( elem );
- destElements = getAll( clone );
-
- // Weird iteration because IE will replace the length property
- // with an element if you are cloning the body and one of the
- // elements on the page has a name or id of "length"
- for ( i = 0; srcElements[i]; ++i ) {
- cloneFixAttributes( srcElements[i], destElements[i] );
- }
- }
-
- // Copy the events from the original to the clone
- if ( dataAndEvents ) {
- cloneCopyEvent( elem, clone );
-
- if ( deepDataAndEvents ) {
- srcElements = getAll( elem );
- destElements = getAll( clone );
-
- for ( i = 0; srcElements[i]; ++i ) {
- cloneCopyEvent( srcElements[i], destElements[i] );
- }
- }
- }
-
- srcElements = destElements = null;
-
- // Return the cloned set
- return clone;
- },
-
- clean: function( elems, context, fragment, scripts ) {
- var checkScriptType;
-
- context = context || document;
-
- // !context.createElement fails in IE with an error but returns typeof 'object'
- if ( typeof context.createElement === "undefined" ) {
- context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
- }
-
- var ret = [], j;
-
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- if ( typeof elem === "number" ) {
- elem += "";
- }
-
- if ( !elem ) {
- continue;
- }
-
- // Convert html string into DOM nodes
- if ( typeof elem === "string" ) {
- if ( !rhtml.test( elem ) ) {
- elem = context.createTextNode( elem );
- } else {
- // Fix "XHTML"-style tags in all browsers
- elem = elem.replace(rxhtmlTag, "<$1></$2>");
-
- // Trim whitespace, otherwise indexOf won't work as expected
- var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
- wrap = wrapMap[ tag ] || wrapMap._default,
- depth = wrap[0],
- div = context.createElement("div");
-
- // Go to html and back, then peel off extra wrappers
- div.innerHTML = wrap[1] + elem + wrap[2];
-
- // Move to the right depth
- while ( depth-- ) {
- div = div.lastChild;
- }
-
- // Remove IE's autoinserted <tbody> from table fragments
- if ( !jQuery.support.tbody ) {
-
- // String was a <table>, *may* have spurious <tbody>
- var hasBody = rtbody.test(elem),
- tbody = tag === "table" && !hasBody ?
- div.firstChild && div.firstChild.childNodes :
-
- // String was a bare <thead> or <tfoot>
- wrap[1] === "<table>" && !hasBody ?
- div.childNodes :
- [];
-
- for ( j = tbody.length - 1; j >= 0 ; --j ) {
- if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
- tbody[ j ].parentNode.removeChild( tbody[ j ] );
- }
- }
- }
-
- // IE completely kills leading whitespace when innerHTML is used
- if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
- div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
- }
-
- elem = div.childNodes;
- }
- }
-
- // Resets defaultChecked for any radios and checkboxes
- // about to be appended to the DOM in IE 6/7 (#8060)
- var len;
- if ( !jQuery.support.appendChecked ) {
- if ( elem[0] && typeof (len = elem.length) === "number" ) {
- for ( j = 0; j < len; j++ ) {
- findInputs( elem[j] );
- }
- } else {
- findInputs( elem );
- }
- }
-
- if ( elem.nodeType ) {
- ret.push( elem );
- } else {
- ret = jQuery.merge( ret, elem );
- }
- }
-
- if ( fragment ) {
- checkScriptType = function( elem ) {
- return !elem.type || rscriptType.test( elem.type );
- };
- for ( i = 0; ret[i]; i++ ) {
- if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
- scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
-
- } else {
- if ( ret[i].nodeType === 1 ) {
- var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType );
-
- ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
- }
- fragment.appendChild( ret[i] );
- }
- }
- }
-
- return ret;
- },
-
- cleanData: function( elems ) {
- var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
- deleteExpando = jQuery.support.deleteExpando;
-
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
- continue;
- }
-
- id = elem[ jQuery.expando ];
-
- if ( id ) {
- data = cache[ id ] && cache[ id ][ internalKey ];
-
- if ( data && data.events ) {
- for ( var type in data.events ) {
- if ( special[ type ] ) {
- jQuery.event.remove( elem, type );
-
- // This is a shortcut to avoid jQuery.event.remove's overhead
- } else {
- jQuery.removeEvent( elem, type, data.handle );
- }
- }
-
- // Null the DOM reference to avoid IE6/7/8 leak (#7054)
- if ( data.handle ) {
- data.handle.elem = null;
- }
- }
-
- if ( deleteExpando ) {
- delete elem[ jQuery.expando ];
-
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
- }
-
- delete cache[ id ];
- }
- }
- }
-});
-
-function evalScript( i, elem ) {
- if ( elem.src ) {
- jQuery.ajax({
- url: elem.src,
- async: false,
- dataType: "script"
- });
- } else {
- jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
- }
-
- if ( elem.parentNode ) {
- elem.parentNode.removeChild( elem );
- }
-}
-
-
-
-var ralpha = /alpha\([^)]*\)/i,
- ropacity = /opacity=([^)]*)/,
- // fixed for IE9, see #8346
- rupper = /([A-Z]|^ms)/g,
- rnumpx = /^-?\d+(?:px)?$/i,
- rnum = /^-?\d/,
- rrelNum = /^[+\-]=/,
- rrelNumFilter = /[^+\-\.\de]+/g,
-
- cssShow = { position: "absolute", visibility: "hidden", display: "block" },
- cssWidth = [ "Left", "Right" ],
- cssHeight = [ "Top", "Bottom" ],
- curCSS,
-
- getComputedStyle,
- currentStyle;
-
-jQuery.fn.css = function( name, value ) {
- // Setting 'undefined' is a no-op
- if ( arguments.length === 2 && value === undefined ) {
- return this;
- }
-
- return jQuery.access( this, name, value, true, function( elem, name, value ) {
- return value !== undefined ?
- jQuery.style( elem, name, value ) :
- jQuery.css( elem, name );
- });
-};
-
-jQuery.extend({
- // Add in style property hooks for overriding the default
- // behavior of getting and setting a style property
- cssHooks: {
- opacity: {
- get: function( elem, computed ) {
- if ( computed ) {
- // We should always get a number back from opacity
- var ret = curCSS( elem, "opacity", "opacity" );
- return ret === "" ? "1" : ret;
-
- } else {
- return elem.style.opacity;
- }
- }
- }
- },
-
- // Exclude the following css properties to add px
- cssNumber: {
- "fillOpacity": true,
- "fontWeight": true,
- "lineHeight": true,
- "opacity": true,
- "orphans": true,
- "widows": true,
- "zIndex": true,
- "zoom": true
- },
-
- // Add in properties whose names you wish to fix before
- // setting or getting the value
- cssProps: {
- // normalize float css property
- "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
- },
-
- // Get and set the style property on a DOM Node
- style: function( elem, name, value, extra ) {
- // Don't set styles on text and comment nodes
- if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
- return;
- }
-
- // Make sure that we're working with the right name
- var ret, type, origName = jQuery.camelCase( name ),
- style = elem.style, hooks = jQuery.cssHooks[ origName ];
-
- name = jQuery.cssProps[ origName ] || origName;
-
- // Check if we're setting a value
- if ( value !== undefined ) {
- type = typeof value;
-
- // Make sure that NaN and null values aren't set. See: #7116
- if ( type === "number" && isNaN( value ) || value == null ) {
- return;
- }
-
- // convert relative number strings (+= or -=) to relative numbers. #7345
- if ( type === "string" && rrelNum.test( value ) ) {
- value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) );
- // Fixes bug #9237
- type = "number";
- }
-
- // If a number was passed in, add 'px' to the (except for certain CSS properties)
- if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
- value += "px";
- }
-
- // If a hook was provided, use that value, otherwise just set the specified value
- if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
- // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
- // Fixes bug #5509
- try {
- style[ name ] = value;
- } catch(e) {}
- }
-
- } else {
- // If a hook was provided get the non-computed value from there
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
- return ret;
- }
-
- // Otherwise just get the value from the style object
- return style[ name ];
- }
- },
-
- css: function( elem, name, extra ) {
- var ret, hooks;
-
- // Make sure that we're working with the right name
- name = jQuery.camelCase( name );
- hooks = jQuery.cssHooks[ name ];
- name = jQuery.cssProps[ name ] || name;
-
- // cssFloat needs a special treatment
- if ( name === "cssFloat" ) {
- name = "float";
- }
-
- // If a hook was provided get the computed value from there
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
- return ret;
-
- // Otherwise, if a way to get the computed value exists, use that
- } else if ( curCSS ) {
- return curCSS( elem, name );
- }
- },
-
- // A method for quickly swapping in/out CSS properties to get correct calculations
- swap: function( elem, options, callback ) {
- var old = {};
-
- // Remember the old values, and insert the new ones
- for ( var name in options ) {
- old[ name ] = elem.style[ name ];
- elem.style[ name ] = options[ name ];
- }
-
- callback.call( elem );
-
- // Revert the old values
- for ( name in options ) {
- elem.style[ name ] = old[ name ];
- }
- }
-});
-
-// DEPRECATED, Use jQuery.css() instead
-jQuery.curCSS = jQuery.css;
-
-jQuery.each(["height", "width"], function( i, name ) {
- jQuery.cssHooks[ name ] = {
- get: function( elem, computed, extra ) {
- var val;
-
- if ( computed ) {
- if ( elem.offsetWidth !== 0 ) {
- return getWH( elem, name, extra );
- } else {
- jQuery.swap( elem, cssShow, function() {
- val = getWH( elem, name, extra );
- });
- }
-
- return val;
- }
- },
-
- set: function( elem, value ) {
- if ( rnumpx.test( value ) ) {
- // ignore negative width and height values #1599
- value = parseFloat( value );
-
- if ( value >= 0 ) {
- return value + "px";
- }
-
- } else {
- return value;
- }
- }
- };
-});
-
-if ( !jQuery.support.opacity ) {
- jQuery.cssHooks.opacity = {
- get: function( elem, computed ) {
- // IE uses filters for opacity
- return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
- ( parseFloat( RegExp.$1 ) / 100 ) + "" :
- computed ? "1" : "";
- },
-
- set: function( elem, value ) {
- var style = elem.style,
- currentStyle = elem.currentStyle;
-
- // IE has trouble with opacity if it does not have layout
- // Force it by setting the zoom level
- style.zoom = 1;
-
- // Set the alpha filter to set the opacity
- var opacity = jQuery.isNaN( value ) ?
- "" :
- "alpha(opacity=" + value * 100 + ")",
- filter = currentStyle && currentStyle.filter || style.filter || "";
-
- style.filter = ralpha.test( filter ) ?
- filter.replace( ralpha, opacity ) :
- filter + " " + opacity;
- }
- };
-}
-
-jQuery(function() {
- // This hook cannot be added until DOM ready because the support test
- // for it is not run until after DOM ready
- if ( !jQuery.support.reliableMarginRight ) {
- jQuery.cssHooks.marginRight = {
- get: function( elem, computed ) {
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- // Work around by temporarily setting element display to inline-block
- var ret;
- jQuery.swap( elem, { "display": "inline-block" }, function() {
- if ( computed ) {
- ret = curCSS( elem, "margin-right", "marginRight" );
- } else {
- ret = elem.style.marginRight;
- }
- });
- return ret;
- }
- };
- }
-});
-
-if ( document.defaultView && document.defaultView.getComputedStyle ) {
- getComputedStyle = function( elem, name ) {
- var ret, defaultView, computedStyle;
-
- name = name.replace( rupper, "-$1" ).toLowerCase();
-
- if ( !(defaultView = elem.ownerDocument.defaultView) ) {
- return undefined;
- }
-
- if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
- ret = computedStyle.getPropertyValue( name );
- if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
- ret = jQuery.style( elem, name );
- }
- }
-
- return ret;
- };
-}
-
-if ( document.documentElement.currentStyle ) {
- currentStyle = function( elem, name ) {
- var left,
- ret = elem.currentStyle && elem.currentStyle[ name ],
- rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
- style = elem.style;
-
- // From the awesome hack by Dean Edwards
- // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
-
- // If we're not dealing with a regular pixel number
- // but a number that has a weird ending, we need to convert it to pixels
- if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
- // Remember the original values
- left = style.left;
-
- // Put in the new values to get a computed value out
- if ( rsLeft ) {
- elem.runtimeStyle.left = elem.currentStyle.left;
- }
- style.left = name === "fontSize" ? "1em" : (ret || 0);
- ret = style.pixelLeft + "px";
-
- // Revert the changed values
- style.left = left;
- if ( rsLeft ) {
- elem.runtimeStyle.left = rsLeft;
- }
- }
-
- return ret === "" ? "auto" : ret;
- };
-}
-
-curCSS = getComputedStyle || currentStyle;
-
-function getWH( elem, name, extra ) {
-
- // Start with offset property
- var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
- which = name === "width" ? cssWidth : cssHeight;
-
- if ( val > 0 ) {
- if ( extra !== "border" ) {
- jQuery.each( which, function() {
- if ( !extra ) {
- val -= parseFloat( jQuery.css( elem, "padding" + this ) ) || 0;
- }
- if ( extra === "margin" ) {
- val += parseFloat( jQuery.css( elem, extra + this ) ) || 0;
- } else {
- val -= parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0;
- }
- });
- }
-
- return val + "px";
- }
-
- // Fall back to computed then uncomputed css if necessary
- val = curCSS( elem, name, name );
- if ( val < 0 || val == null ) {
- val = elem.style[ name ] || 0;
- }
- // Normalize "", auto, and prepare for extra
- val = parseFloat( val ) || 0;
-
- // Add padding, border, margin
- if ( extra ) {
- jQuery.each( which, function() {
- val += parseFloat( jQuery.css( elem, "padding" + this ) ) || 0;
- if ( extra !== "padding" ) {
- val += parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0;
- }
- if ( extra === "margin" ) {
- val += parseFloat( jQuery.css( elem, extra + this ) ) || 0;
- }
- });
- }
-
- return val + "px";
-}
-
-if ( jQuery.expr && jQuery.expr.filters ) {
- jQuery.expr.filters.hidden = function( elem ) {
- var width = elem.offsetWidth,
- height = elem.offsetHeight;
-
- return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
- };
-
- jQuery.expr.filters.visible = function( elem ) {
- return !jQuery.expr.filters.hidden( elem );
- };
-}
-
-
-
-
-var r20 = /%20/g,
- rbracket = /\[\]$/,
- rCRLF = /\r?\n/g,
- rhash = /#.*$/,
- rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
- rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
- // #7653, #8125, #8152: local protocol detection
- rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|widget):$/,
- rnoContent = /^(?:GET|HEAD)$/,
- rprotocol = /^\/\//,
- rquery = /\?/,
- rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
- rselectTextarea = /^(?:select|textarea)/i,
- rspacesAjax = /\s+/,
- rts = /([?&])_=[^&]*/,
- rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
-
- // Keep a copy of the old load method
- _load = jQuery.fn.load,
-
- /* Prefilters
- * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
- * 2) These are called:
- * - BEFORE asking for a transport
- * - AFTER param serialization (s.data is a string if s.processData is true)
- * 3) key is the dataType
- * 4) the catchall symbol "*" can be used
- * 5) execution will start with transport dataType and THEN continue down to "*" if needed
- */
- prefilters = {},
-
- /* Transports bindings
- * 1) key is the dataType
- * 2) the catchall symbol "*" can be used
- * 3) selection will start with transport dataType and THEN go to "*" if needed
- */
- transports = {},
-
- // Document location
- ajaxLocation,
-
- // Document location segments
- ajaxLocParts;
-
-// #8138, IE may throw an exception when accessing
-// a field from window.location if document.domain has been set
-try {
- ajaxLocation = location.href;
-} catch( e ) {
- // Use the href attribute of an A element
- // since IE will modify it given document.location
- ajaxLocation = document.createElement( "a" );
- ajaxLocation.href = "";
- ajaxLocation = ajaxLocation.href;
-}
-
-// Segment location into parts
-ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
-
-// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
-function addToPrefiltersOrTransports( structure ) {
-
- // dataTypeExpression is optional and defaults to "*"
- return function( dataTypeExpression, func ) {
-
- if ( typeof dataTypeExpression !== "string" ) {
- func = dataTypeExpression;
- dataTypeExpression = "*";
- }
-
- if ( jQuery.isFunction( func ) ) {
- var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
- i = 0,
- length = dataTypes.length,
- dataType,
- list,
- placeBefore;
-
- // For each dataType in the dataTypeExpression
- for(; i < length; i++ ) {
- dataType = dataTypes[ i ];
- // We control if we're asked to add before
- // any existing element
- placeBefore = /^\+/.test( dataType );
- if ( placeBefore ) {
- dataType = dataType.substr( 1 ) || "*";
- }
- list = structure[ dataType ] = structure[ dataType ] || [];
- // then we add to the structure accordingly
- list[ placeBefore ? "unshift" : "push" ]( func );
- }
- }
- };
-}
-
-// Base inspection function for prefilters and transports
-function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
- dataType /* internal */, inspected /* internal */ ) {
-
- dataType = dataType || options.dataTypes[ 0 ];
- inspected = inspected || {};
-
- inspected[ dataType ] = true;
-
- var list = structure[ dataType ],
- i = 0,
- length = list ? list.length : 0,
- executeOnly = ( structure === prefilters ),
- selection;
-
- for(; i < length && ( executeOnly || !selection ); i++ ) {
- selection = list[ i ]( options, originalOptions, jqXHR );
- // If we got redirected to another dataType
- // we try there if executing only and not done already
- if ( typeof selection === "string" ) {
- if ( !executeOnly || inspected[ selection ] ) {
- selection = undefined;
- } else {
- options.dataTypes.unshift( selection );
- selection = inspectPrefiltersOrTransports(
- structure, options, originalOptions, jqXHR, selection, inspected );
- }
- }
- }
- // If we're only executing or nothing was selected
- // we try the catchall dataType if not done already
- if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
- selection = inspectPrefiltersOrTransports(
- structure, options, originalOptions, jqXHR, "*", inspected );
- }
- // unnecessary when only executing (prefilters)
- // but it'll be ignored by the caller in that case
- return selection;
-}
-
-jQuery.fn.extend({
- load: function( url, params, callback ) {
- if ( typeof url !== "string" && _load ) {
- return _load.apply( this, arguments );
-
- // Don't do a request if no elements are being requested
- } else if ( !this.length ) {
- return this;
- }
-
- var off = url.indexOf( " " );
- if ( off >= 0 ) {
- var selector = url.slice( off, url.length );
- url = url.slice( 0, off );
- }
-
- // Default to a GET request
- var type = "GET";
-
- // If the second parameter was provided
- if ( params ) {
- // If it's a function
- if ( jQuery.isFunction( params ) ) {
- // We assume that it's the callback
- callback = params;
- params = undefined;
-
- // Otherwise, build a param string
- } else if ( typeof params === "object" ) {
- params = jQuery.param( params, jQuery.ajaxSettings.traditional );
- type = "POST";
- }
- }
-
- var self = this;
-
- // Request the remote document
- jQuery.ajax({
- url: url,
- type: type,
- dataType: "html",
- data: params,
- // Complete callback (responseText is used internally)
- complete: function( jqXHR, status, responseText ) {
- // Store the response as specified by the jqXHR object
- responseText = jqXHR.responseText;
- // If successful, inject the HTML into all the matched elements
- if ( jqXHR.isResolved() ) {
- // #4825: Get the actual response in case
- // a dataFilter is present in ajaxSettings
- jqXHR.done(function( r ) {
- responseText = r;
- });
- // See if a selector was specified
- self.html( selector ?
- // Create a dummy div to hold the results
- jQuery("<div>")
- // inject the contents of the document in, removing the scripts
- // to avoid any 'Permission Denied' errors in IE
- .append(responseText.replace(rscript, ""))
-
- // Locate the specified elements
- .find(selector) :
-
- // If not, just inject the full result
- responseText );
- }
-
- if ( callback ) {
- self.each( callback, [ responseText, status, jqXHR ] );
- }
- }
- });
-
- return this;
- },
-
- serialize: function() {
- return jQuery.param( this.serializeArray() );
- },
-
- serializeArray: function() {
- return this.map(function(){
- return this.elements ? jQuery.makeArray( this.elements ) : this;
- })
- .filter(function(){
- return this.name && !this.disabled &&
- ( this.checked || rselectTextarea.test( this.nodeName ) ||
- rinput.test( this.type ) );
- })
- .map(function( i, elem ){
- var val = jQuery( this ).val();
-
- return val == null ?
- null :
- jQuery.isArray( val ) ?
- jQuery.map( val, function( val, i ){
- return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
- }) :
- { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
- }).get();
- }
-});
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
- jQuery.fn[ o ] = function( f ){
- return this.bind( o, f );
- };
-});
-
-jQuery.each( [ "get", "post" ], function( i, method ) {
- jQuery[ method ] = function( url, data, callback, type ) {
- // shift arguments if data argument was omitted
- if ( jQuery.isFunction( data ) ) {
- type = type || callback;
- callback = data;
- data = undefined;
- }
-
- return jQuery.ajax({
- type: method,
- url: url,
- data: data,
- success: callback,
- dataType: type
- });
- };
-});
-
-jQuery.extend({
-
- getScript: function( url, callback ) {
- return jQuery.get( url, undefined, callback, "script" );
- },
-
- getJSON: function( url, data, callback ) {
- return jQuery.get( url, data, callback, "json" );
- },
-
- // Creates a full fledged settings object into target
- // with both ajaxSettings and settings fields.
- // If target is omitted, writes into ajaxSettings.
- ajaxSetup: function ( target, settings ) {
- if ( !settings ) {
- // Only one parameter, we extend ajaxSettings
- settings = target;
- target = jQuery.extend( true, jQuery.ajaxSettings, settings );
- } else {
- // target was provided, we extend into it
- jQuery.extend( true, target, jQuery.ajaxSettings, settings );
- }
- // Flatten fields we don't want deep extended
- for( var field in { context: 1, url: 1 } ) {
- if ( field in settings ) {
- target[ field ] = settings[ field ];
- } else if( field in jQuery.ajaxSettings ) {
- target[ field ] = jQuery.ajaxSettings[ field ];
- }
- }
- return target;
- },
-
- ajaxSettings: {
- url: ajaxLocation,
- isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
- global: true,
- type: "GET",
- contentType: "application/x-www-form-urlencoded",
- processData: true,
- async: true,
- /*
- timeout: 0,
- data: null,
- dataType: null,
- username: null,
- password: null,
- cache: null,
- traditional: false,
- headers: {},
- */
-
- accepts: {
- xml: "application/xml, text/xml",
- html: "text/html",
- text: "text/plain",
- json: "application/json, text/javascript",
- "*": "*/*"
- },
-
- contents: {
- xml: /xml/,
- html: /html/,
- json: /json/
- },
-
- responseFields: {
- xml: "responseXML",
- text: "responseText"
- },
-
- // List of data converters
- // 1) key format is "source_type destination_type" (a single space in-between)
- // 2) the catchall symbol "*" can be used for source_type
- converters: {
-
- // Convert anything to text
- "* text": window.String,
-
- // Text to html (true = no transformation)
- "text html": true,
-
- // Evaluate text as a json expression
- "text json": jQuery.parseJSON,
-
- // Parse text as xml
- "text xml": jQuery.parseXML
- }
- },
-
- ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
- ajaxTransport: addToPrefiltersOrTransports( transports ),
-
- // Main method
- ajax: function( url, options ) {
-
- // If url is an object, simulate pre-1.5 signature
- if ( typeof url === "object" ) {
- options = url;
- url = undefined;
- }
-
- // Force options to be an object
- options = options || {};
-
- var // Create the final options object
- s = jQuery.ajaxSetup( {}, options ),
- // Callbacks context
- callbackContext = s.context || s,
- // Context for global events
- // It's the callbackContext if one was provided in the options
- // and if it's a DOM node or a jQuery collection
- globalEventContext = callbackContext !== s &&
- ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
- jQuery( callbackContext ) : jQuery.event,
- // Deferreds
- deferred = jQuery.Deferred(),
- completeDeferred = jQuery._Deferred(),
- // Status-dependent callbacks
- statusCode = s.statusCode || {},
- // ifModified key
- ifModifiedKey,
- // Headers (they are sent all at once)
- requestHeaders = {},
- requestHeadersNames = {},
- // Response headers
- responseHeadersString,
- responseHeaders,
- // transport
- transport,
- // timeout handle
- timeoutTimer,
- // Cross-domain detection vars
- parts,
- // The jqXHR state
- state = 0,
- // To know if global events are to be dispatched
- fireGlobals,
- // Loop variable
- i,
- // Fake xhr
- jqXHR = {
-
- readyState: 0,
-
- // Caches the header
- setRequestHeader: function( name, value ) {
- if ( !state ) {
- var lname = name.toLowerCase();
- name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
- requestHeaders[ name ] = value;
- }
- return this;
- },
-
- // Raw string
- getAllResponseHeaders: function() {
- return state === 2 ? responseHeadersString : null;
- },
-
- // Builds headers hashtable if needed
- getResponseHeader: function( key ) {
- var match;
- if ( state === 2 ) {
- if ( !responseHeaders ) {
- responseHeaders = {};
- while( ( match = rheaders.exec( responseHeadersString ) ) ) {
- responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
- }
- }
- match = responseHeaders[ key.toLowerCase() ];
- }
- return match === undefined ? null : match;
- },
-
- // Overrides response content-type header
- overrideMimeType: function( type ) {
- if ( !state ) {
- s.mimeType = type;
- }
- return this;
- },
-
- // Cancel the request
- abort: function( statusText ) {
- statusText = statusText || "abort";
- if ( transport ) {
- transport.abort( statusText );
- }
- done( 0, statusText );
- return this;
- }
- };
-
- // Callback for when everything is done
- // It is defined here because jslint complains if it is declared
- // at the end of the function (which would be more logical and readable)
- function done( status, statusText, responses, headers ) {
-
- // Called once
- if ( state === 2 ) {
- return;
- }
-
- // State is "done" now
- state = 2;
-
- // Clear timeout if it exists
- if ( timeoutTimer ) {
- clearTimeout( timeoutTimer );
- }
-
- // Dereference transport for early garbage collection
- // (no matter how long the jqXHR object will be used)
- transport = undefined;
-
- // Cache response headers
- responseHeadersString = headers || "";
-
- // Set readyState
- jqXHR.readyState = status ? 4 : 0;
-
- var isSuccess,
- success,
- error,
- response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
- lastModified,
- etag;
-
- // If successful, handle type chaining
- if ( status >= 200 && status < 300 || status === 304 ) {
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if ( s.ifModified ) {
-
- if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
- jQuery.lastModified[ ifModifiedKey ] = lastModified;
- }
- if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
- jQuery.etag[ ifModifiedKey ] = etag;
- }
- }
-
- // If not modified
- if ( status === 304 ) {
-
- statusText = "notmodified";
- isSuccess = true;
-
- // If we have data
- } else {
-
- try {
- success = ajaxConvert( s, response );
- statusText = "success";
- isSuccess = true;
- } catch(e) {
- // We have a parsererror
- statusText = "parsererror";
- error = e;
- }
- }
- } else {
- // We extract error from statusText
- // then normalize statusText and status for non-aborts
- error = statusText;
- if( !statusText || status ) {
- statusText = "error";
- if ( status < 0 ) {
- status = 0;
- }
- }
- }
-
- // Set data for the fake xhr object
- jqXHR.status = status;
- jqXHR.statusText = statusText;
-
- // Success/Error
- if ( isSuccess ) {
- deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
- } else {
- deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
- }
-
- // Status-dependent callbacks
- jqXHR.statusCode( statusCode );
- statusCode = undefined;
-
- if ( fireGlobals ) {
- globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
- [ jqXHR, s, isSuccess ? success : error ] );
- }
-
- // Complete
- completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
-
- if ( fireGlobals ) {
- globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
- // Handle the global AJAX counter
- if ( !( --jQuery.active ) ) {
- jQuery.event.trigger( "ajaxStop" );
- }
- }
- }
-
- // Attach deferreds
- deferred.promise( jqXHR );
- jqXHR.success = jqXHR.done;
- jqXHR.error = jqXHR.fail;
- jqXHR.complete = completeDeferred.done;
-
- // Status-dependent callbacks
- jqXHR.statusCode = function( map ) {
- if ( map ) {
- var tmp;
- if ( state < 2 ) {
- for( tmp in map ) {
- statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
- }
- } else {
- tmp = map[ jqXHR.status ];
- jqXHR.then( tmp, tmp );
- }
- }
- return this;
- };
-
- // Remove hash character (#7531: and string promotion)
- // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
- // We also use the url parameter if available
- s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
-
- // Extract dataTypes list
- s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
-
- // Determine if a cross-domain request is in order
- if ( s.crossDomain == null ) {
- parts = rurl.exec( s.url.toLowerCase() );
- s.crossDomain = !!( parts &&
- ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
- ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
- ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
- );
- }
-
- // Convert data if not already a string
- if ( s.data && s.processData && typeof s.data !== "string" ) {
- s.data = jQuery.param( s.data, s.traditional );
- }
-
- // Apply prefilters
- inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
-
- // If request was aborted inside a prefiler, stop there
- if ( state === 2 ) {
- return false;
- }
-
- // We can fire global events as of now if asked to
- fireGlobals = s.global;
-
- // Uppercase the type
- s.type = s.type.toUpperCase();
-
- // Determine if request has content
- s.hasContent = !rnoContent.test( s.type );
-
- // Watch for a new set of requests
- if ( fireGlobals && jQuery.active++ === 0 ) {
- jQuery.event.trigger( "ajaxStart" );
- }
-
- // More options handling for requests with no content
- if ( !s.hasContent ) {
-
- // If data is available, append data to url
- if ( s.data ) {
- s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
- }
-
- // Get ifModifiedKey before adding the anti-cache parameter
- ifModifiedKey = s.url;
-
- // Add anti-cache in url if needed
- if ( s.cache === false ) {
-
- var ts = jQuery.now(),
- // try replacing _= if it is there
- ret = s.url.replace( rts, "$1_=" + ts );
-
- // if nothing was replaced, add timestamp to the end
- s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
- }
- }
-
- // Set the correct header, if data is being sent
- if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
- jqXHR.setRequestHeader( "Content-Type", s.contentType );
- }
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if ( s.ifModified ) {
- ifModifiedKey = ifModifiedKey || s.url;
- if ( jQuery.lastModified[ ifModifiedKey ] ) {
- jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
- }
- if ( jQuery.etag[ ifModifiedKey ] ) {
- jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
- }
- }
-
- // Set the Accepts header for the server, depending on the dataType
- jqXHR.setRequestHeader(
- "Accept",
- s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
- s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
- s.accepts[ "*" ]
- );
-
- // Check for headers option
- for ( i in s.headers ) {
- jqXHR.setRequestHeader( i, s.headers[ i ] );
- }
-
- // Allow custom headers/mimetypes and early abort
- if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
- // Abort if not done already
- jqXHR.abort();
- return false;
-
- }
-
- // Install callbacks on deferreds
- for ( i in { success: 1, error: 1, complete: 1 } ) {
- jqXHR[ i ]( s[ i ] );
- }
-
- // Get transport
- transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
-
- // If no transport, we auto-abort
- if ( !transport ) {
- done( -1, "No Transport" );
- } else {
- jqXHR.readyState = 1;
- // Send global event
- if ( fireGlobals ) {
- globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
- }
- // Timeout
- if ( s.async && s.timeout > 0 ) {
- timeoutTimer = setTimeout( function(){
- jqXHR.abort( "timeout" );
- }, s.timeout );
- }
-
- try {
- state = 1;
- transport.send( requestHeaders, done );
- } catch (e) {
- // Propagate exception as error if not done
- if ( status < 2 ) {
- done( -1, e );
- // Simply rethrow otherwise
- } else {
- jQuery.error( e );
- }
- }
- }
-
- return jqXHR;
- },
-
- // Serialize an array of form elements or a set of
- // key/values into a query string
- param: function( a, traditional ) {
- var s = [],
- add = function( key, value ) {
- // If value is a function, invoke it and return its value
- value = jQuery.isFunction( value ) ? value() : value;
- s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
- };
-
- // Set traditional to true for jQuery <= 1.3.2 behavior.
- if ( traditional === undefined ) {
- traditional = jQuery.ajaxSettings.traditional;
- }
-
- // If an array was passed in, assume that it is an array of form elements.
- if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
- // Serialize the form elements
- jQuery.each( a, function() {
- add( this.name, this.value );
- });
-
- } else {
- // If traditional, encode the "old" way (the way 1.3.2 or older
- // did it), otherwise encode params recursively.
- for ( var prefix in a ) {
- buildParams( prefix, a[ prefix ], traditional, add );
- }
- }
-
- // Return the resulting serialization
- return s.join( "&" ).replace( r20, "+" );
- }
-});
-
-function buildParams( prefix, obj, traditional, add ) {
- if ( jQuery.isArray( obj ) ) {
- // Serialize array item.
- jQuery.each( obj, function( i, v ) {
- if ( traditional || rbracket.test( prefix ) ) {
- // Treat each array item as a scalar.
- add( prefix, v );
-
- } else {
- // If array item is non-scalar (array or object), encode its
- // numeric index to resolve deserialization ambiguity issues.
- // Note that rack (as of 1.0.0) can't currently deserialize
- // nested arrays properly, and attempting to do so may cause
- // a server error. Possible fixes are to modify rack's
- // deserialization algorithm or to provide an option or flag
- // to force array serialization to be shallow.
- buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
- }
- });
-
- } else if ( !traditional && obj != null && typeof obj === "object" ) {
- // Serialize object item.
- for ( var name in obj ) {
- buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
- }
-
- } else {
- // Serialize scalar item.
- add( prefix, obj );
- }
-}
-
-// This is still on the jQuery object... for now
-// Want to move this to jQuery.ajax some day
-jQuery.extend({
-
- // Counter for holding the number of active queries
- active: 0,
-
- // Last-Modified header cache for next request
- lastModified: {},
- etag: {}
-
-});
-
-/* Handles responses to an ajax request:
- * - sets all responseXXX fields accordingly
- * - finds the right dataType (mediates between content-type and expected dataType)
- * - returns the corresponding response
- */
-function ajaxHandleResponses( s, jqXHR, responses ) {
-
- var contents = s.contents,
- dataTypes = s.dataTypes,
- responseFields = s.responseFields,
- ct,
- type,
- finalDataType,
- firstDataType;
-
- // Fill responseXXX fields
- for( type in responseFields ) {
- if ( type in responses ) {
- jqXHR[ responseFields[type] ] = responses[ type ];
- }
- }
-
- // Remove auto dataType and get content-type in the process
- while( dataTypes[ 0 ] === "*" ) {
- dataTypes.shift();
- if ( ct === undefined ) {
- ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
- }
- }
-
- // Check if we're dealing with a known content-type
- if ( ct ) {
- for ( type in contents ) {
- if ( contents[ type ] && contents[ type ].test( ct ) ) {
- dataTypes.unshift( type );
- break;
- }
- }
- }
-
- // Check to see if we have a response for the expected dataType
- if ( dataTypes[ 0 ] in responses ) {
- finalDataType = dataTypes[ 0 ];
- } else {
- // Try convertible dataTypes
- for ( type in responses ) {
- if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
- finalDataType = type;
- break;
- }
- if ( !firstDataType ) {
- firstDataType = type;
- }
- }
- // Or just use first one
- finalDataType = finalDataType || firstDataType;
- }
-
- // If we found a dataType
- // We add the dataType to the list if needed
- // and return the corresponding response
- if ( finalDataType ) {
- if ( finalDataType !== dataTypes[ 0 ] ) {
- dataTypes.unshift( finalDataType );
- }
- return responses[ finalDataType ];
- }
-}
-
-// Chain conversions given the request and the original response
-function ajaxConvert( s, response ) {
-
- // Apply the dataFilter if provided
- if ( s.dataFilter ) {
- response = s.dataFilter( response, s.dataType );
- }
-
- var dataTypes = s.dataTypes,
- converters = {},
- i,
- key,
- length = dataTypes.length,
- tmp,
- // Current and previous dataTypes
- current = dataTypes[ 0 ],
- prev,
- // Conversion expression
- conversion,
- // Conversion function
- conv,
- // Conversion functions (transitive conversion)
- conv1,
- conv2;
-
- // For each dataType in the chain
- for( i = 1; i < length; i++ ) {
-
- // Create converters map
- // with lowercased keys
- if ( i === 1 ) {
- for( key in s.converters ) {
- if( typeof key === "string" ) {
- converters[ key.toLowerCase() ] = s.converters[ key ];
- }
- }
- }
-
- // Get the dataTypes
- prev = current;
- current = dataTypes[ i ];
-
- // If current is auto dataType, update it to prev
- if( current === "*" ) {
- current = prev;
- // If no auto and dataTypes are actually different
- } else if ( prev !== "*" && prev !== current ) {
-
- // Get the converter
- conversion = prev + " " + current;
- conv = converters[ conversion ] || converters[ "* " + current ];
-
- // If there is no direct converter, search transitively
- if ( !conv ) {
- conv2 = undefined;
- for( conv1 in converters ) {
- tmp = conv1.split( " " );
- if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
- conv2 = converters[ tmp[1] + " " + current ];
- if ( conv2 ) {
- conv1 = converters[ conv1 ];
- if ( conv1 === true ) {
- conv = conv2;
- } else if ( conv2 === true ) {
- conv = conv1;
- }
- break;
- }
- }
- }
- }
- // If we found no converter, dispatch an error
- if ( !( conv || conv2 ) ) {
- jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
- }
- // If found converter is not an equivalence
- if ( conv !== true ) {
- // Convert with 1 or 2 converters accordingly
- response = conv ? conv( response ) : conv2( conv1(response) );
- }
- }
- }
- return response;
-}
-
-
-
-
-var jsc = jQuery.now(),
- jsre = /(\=)\?(&|$)|\?\?/i;
-
-// Default jsonp settings
-jQuery.ajaxSetup({
- jsonp: "callback",
- jsonpCallback: function() {
- return jQuery.expando + "_" + ( jsc++ );
- }
-});
-
-// Detect, normalize options and install callbacks for jsonp requests
-jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
-
- var inspectData = s.contentType === "application/x-www-form-urlencoded" &&
- ( typeof s.data === "string" );
-
- if ( s.dataTypes[ 0 ] === "jsonp" ||
- s.jsonp !== false && ( jsre.test( s.url ) ||
- inspectData && jsre.test( s.data ) ) ) {
-
- var responseContainer,
- jsonpCallback = s.jsonpCallback =
- jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
- previous = window[ jsonpCallback ],
- url = s.url,
- data = s.data,
- replace = "$1" + jsonpCallback + "$2";
-
- if ( s.jsonp !== false ) {
- url = url.replace( jsre, replace );
- if ( s.url === url ) {
- if ( inspectData ) {
- data = data.replace( jsre, replace );
- }
- if ( s.data === data ) {
- // Add callback manually
- url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
- }
- }
- }
-
- s.url = url;
- s.data = data;
-
- // Install callback
- window[ jsonpCallback ] = function( response ) {
- responseContainer = [ response ];
- };
-
- // Clean-up function
- jqXHR.always(function() {
- // Set callback back to previous value
- window[ jsonpCallback ] = previous;
- // Call if it was a function and we have a response
- if ( responseContainer && jQuery.isFunction( previous ) ) {
- window[ jsonpCallback ]( responseContainer[ 0 ] );
- }
- });
-
- // Use data converter to retrieve json after script execution
- s.converters["script json"] = function() {
- if ( !responseContainer ) {
- jQuery.error( jsonpCallback + " was not called" );
- }
- return responseContainer[ 0 ];
- };
-
- // force json dataType
- s.dataTypes[ 0 ] = "json";
-
- // Delegate to script
- return "script";
- }
-});
-
-
-
-
-// Install script dataType
-jQuery.ajaxSetup({
- accepts: {
- script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
- },
- contents: {
- script: /javascript|ecmascript/
- },
- converters: {
- "text script": function( text ) {
- jQuery.globalEval( text );
- return text;
- }
- }
-});
-
-// Handle cache's special case and global
-jQuery.ajaxPrefilter( "script", function( s ) {
- if ( s.cache === undefined ) {
- s.cache = false;
- }
- if ( s.crossDomain ) {
- s.type = "GET";
- s.global = false;
- }
-});
-
-// Bind script tag hack transport
-jQuery.ajaxTransport( "script", function(s) {
-
- // This transport only deals with cross domain requests
- if ( s.crossDomain ) {
-
- var script,
- head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
-
- return {
-
- send: function( _, callback ) {
-
- script = document.createElement( "script" );
-
- script.async = "async";
-
- if ( s.scriptCharset ) {
- script.charset = s.scriptCharset;
- }
-
- script.src = s.url;
-
- // Attach handlers for all browsers
- script.onload = script.onreadystatechange = function( _, isAbort ) {
-
- if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
-
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
-
- // Remove the script
- if ( head && script.parentNode ) {
- head.removeChild( script );
- }
-
- // Dereference the script
- script = undefined;
-
- // Callback if not abort
- if ( !isAbort ) {
- callback( 200, "success" );
- }
- }
- };
- // Use insertBefore instead of appendChild to circumvent an IE6 bug.
- // This arises when a base node is used (#2709 and #4378).
- head.insertBefore( script, head.firstChild );
- },
-
- abort: function() {
- if ( script ) {
- script.onload( 0, 1 );
- }
- }
- };
- }
-});
-
-
-
-
-var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
- xhrOnUnloadAbort = window.ActiveXObject ? function() {
- // Abort all pending requests
- for ( var key in xhrCallbacks ) {
- xhrCallbacks[ key ]( 0, 1 );
- }
- } : false,
- xhrId = 0,
- xhrCallbacks;
-
-// Functions to create xhrs
-function createStandardXHR() {
- try {
- return new window.XMLHttpRequest();
- } catch( e ) {}
-}
-
-function createActiveXHR() {
- try {
- return new window.ActiveXObject( "Microsoft.XMLHTTP" );
- } catch( e ) {}
-}
-
-// Create the request object
-// (This is still attached to ajaxSettings for backward compatibility)
-jQuery.ajaxSettings.xhr = window.ActiveXObject ?
- /* Microsoft failed to properly
- * implement the XMLHttpRequest in IE7 (can't request local files),
- * so we use the ActiveXObject when it is available
- * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
- * we need a fallback.
- */
- function() {
- return !this.isLocal && createStandardXHR() || createActiveXHR();
- } :
- // For all other browsers, use the standard XMLHttpRequest object
- createStandardXHR;
-
-// Determine support properties
-(function( xhr ) {
- jQuery.extend( jQuery.support, {
- ajax: !!xhr,
- cors: !!xhr && ( "withCredentials" in xhr )
- });
-})( jQuery.ajaxSettings.xhr() );
-
-// Create transport if the browser can provide an xhr
-if ( jQuery.support.ajax ) {
-
- jQuery.ajaxTransport(function( s ) {
- // Cross domain only allowed if supported through XMLHttpRequest
- if ( !s.crossDomain || jQuery.support.cors ) {
-
- var callback;
-
- return {
- send: function( headers, complete ) {
-
- // Get a new xhr
- var xhr = s.xhr(),
- handle,
- i;
-
- // Open the socket
- // Passing null username, generates a login popup on Opera (#2865)
- if ( s.username ) {
- xhr.open( s.type, s.url, s.async, s.username, s.password );
- } else {
- xhr.open( s.type, s.url, s.async );
- }
-
- // Apply custom fields if provided
- if ( s.xhrFields ) {
- for ( i in s.xhrFields ) {
- xhr[ i ] = s.xhrFields[ i ];
- }
- }
-
- // Override mime type if needed
- if ( s.mimeType && xhr.overrideMimeType ) {
- xhr.overrideMimeType( s.mimeType );
- }
-
- // X-Requested-With header
- // For cross-domain requests, seeing as conditions for a preflight are
- // akin to a jigsaw puzzle, we simply never set it to be sure.
- // (it can always be set on a per-request basis or even using ajaxSetup)
- // For same-domain requests, won't change header if already provided.
- if ( !s.crossDomain && !headers["X-Requested-With"] ) {
- headers[ "X-Requested-With" ] = "XMLHttpRequest";
- }
-
- // Need an extra try/catch for cross domain requests in Firefox 3
- try {
- for ( i in headers ) {
- xhr.setRequestHeader( i, headers[ i ] );
- }
- } catch( _ ) {}
-
- // Do send the request
- // This may raise an exception which is actually
- // handled in jQuery.ajax (so no try/catch here)
- xhr.send( ( s.hasContent && s.data ) || null );
-
- // Listener
- callback = function( _, isAbort ) {
-
- var status,
- statusText,
- responseHeaders,
- responses,
- xml;
-
- // Firefox throws exceptions when accessing properties
- // of an xhr when a network error occured
- // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
- try {
-
- // Was never called and is aborted or complete
- if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
-
- // Only called once
- callback = undefined;
-
- // Do not keep as active anymore
- if ( handle ) {
- xhr.onreadystatechange = jQuery.noop;
- if ( xhrOnUnloadAbort ) {
- delete xhrCallbacks[ handle ];
- }
- }
-
- // If it's an abort
- if ( isAbort ) {
- // Abort it manually if needed
- if ( xhr.readyState !== 4 ) {
- xhr.abort();
- }
- } else {
- status = xhr.status;
- responseHeaders = xhr.getAllResponseHeaders();
- responses = {};
- xml = xhr.responseXML;
-
- // Construct response list
- if ( xml && xml.documentElement /* #4958 */ ) {
- responses.xml = xml;
- }
- responses.text = xhr.responseText;
-
- // Firefox throws an exception when accessing
- // statusText for faulty cross-domain requests
- try {
- statusText = xhr.statusText;
- } catch( e ) {
- // We normalize with Webkit giving an empty statusText
- statusText = "";
- }
-
- // Filter status for non standard behaviors
-
- // If the request is local and we have data: assume a success
- // (success with no data won't get notified, that's the best we
- // can do given current implementations)
- if ( !status && s.isLocal && !s.crossDomain ) {
- status = responses.text ? 200 : 404;
- // IE - #1450: sometimes returns 1223 when it should be 204
- } else if ( status === 1223 ) {
- status = 204;
- }
- }
- }
- } catch( firefoxAccessException ) {
- if ( !isAbort ) {
- complete( -1, firefoxAccessException );
- }
- }
-
- // Call complete if needed
- if ( responses ) {
- complete( status, statusText, responses, responseHeaders );
- }
- };
-
- // if we're in sync mode or it's in cache
- // and has been retrieved directly (IE6 & IE7)
- // we need to manually fire the callback
- if ( !s.async || xhr.readyState === 4 ) {
- callback();
- } else {
- handle = ++xhrId;
- if ( xhrOnUnloadAbort ) {
- // Create the active xhrs callbacks list if needed
- // and attach the unload handler
- if ( !xhrCallbacks ) {
- xhrCallbacks = {};
- jQuery( window ).unload( xhrOnUnloadAbort );
- }
- // Add to list of active xhrs callbacks
- xhrCallbacks[ handle ] = callback;
- }
- xhr.onreadystatechange = callback;
- }
- },
-
- abort: function() {
- if ( callback ) {
- callback(0,1);
- }
- }
- };
- }
- });
-}
-
-
-
-
-var elemdisplay = {},
- iframe, iframeDoc,
- rfxtypes = /^(?:toggle|show|hide)$/,
- rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
- timerId,
- fxAttrs = [
- // height animations
- [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
- // width animations
- [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
- // opacity animations
- [ "opacity" ]
- ],
- fxNow,
- requestAnimationFrame = window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame;
-
-jQuery.fn.extend({
- show: function( speed, easing, callback ) {
- var elem, display;
-
- if ( speed || speed === 0 ) {
- return this.animate( genFx("show", 3), speed, easing, callback);
-
- } else {
- for ( var i = 0, j = this.length; i < j; i++ ) {
- elem = this[i];
-
- if ( elem.style ) {
- display = elem.style.display;
-
- // Reset the inline display of this element to learn if it is
- // being hidden by cascaded rules or not
- if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
- display = elem.style.display = "";
- }
-
- // Set elements which have been overridden with display: none
- // in a stylesheet to whatever the default browser style is
- // for such an element
- if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
- jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
- }
- }
- }
-
- // Set the display of most of the elements in a second loop
- // to avoid the constant reflow
- for ( i = 0; i < j; i++ ) {
- elem = this[i];
-
- if ( elem.style ) {
- display = elem.style.display;
-
- if ( display === "" || display === "none" ) {
- elem.style.display = jQuery._data(elem, "olddisplay") || "";
- }
- }
- }
-
- return this;
- }
- },
-
- hide: function( speed, easing, callback ) {
- if ( speed || speed === 0 ) {
- return this.animate( genFx("hide", 3), speed, easing, callback);
-
- } else {
- for ( var i = 0, j = this.length; i < j; i++ ) {
- if ( this[i].style ) {
- var display = jQuery.css( this[i], "display" );
-
- if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
- jQuery._data( this[i], "olddisplay", display );
- }
- }
- }
-
- // Set the display of the elements in a second loop
- // to avoid the constant reflow
- for ( i = 0; i < j; i++ ) {
- if ( this[i].style ) {
- this[i].style.display = "none";
- }
- }
-
- return this;
- }
- },
-
- // Save the old toggle function
- _toggle: jQuery.fn.toggle,
-
- toggle: function( fn, fn2, callback ) {
- var bool = typeof fn === "boolean";
-
- if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
- this._toggle.apply( this, arguments );
-
- } else if ( fn == null || bool ) {
- this.each(function() {
- var state = bool ? fn : jQuery(this).is(":hidden");
- jQuery(this)[ state ? "show" : "hide" ]();
- });
-
- } else {
- this.animate(genFx("toggle", 3), fn, fn2, callback);
- }
-
- return this;
- },
-
- fadeTo: function( speed, to, easing, callback ) {
- return this.filter(":hidden").css("opacity", 0).show().end()
- .animate({opacity: to}, speed, easing, callback);
- },
-
- animate: function( prop, speed, easing, callback ) {
- var optall = jQuery.speed(speed, easing, callback);
-
- if ( jQuery.isEmptyObject( prop ) ) {
- return this.each( optall.complete, [ false ] );
- }
-
- // Do not change referenced properties as per-property easing will be lost
- prop = jQuery.extend( {}, prop );
-
- return this[ optall.queue === false ? "each" : "queue" ](function() {
- // XXX 'this' does not always have a nodeName when running the
- // test suite
-
- if ( optall.queue === false ) {
- jQuery._mark( this );
- }
-
- var opt = jQuery.extend( {}, optall ),
- isElement = this.nodeType === 1,
- hidden = isElement && jQuery(this).is(":hidden"),
- name, val, p,
- display, e,
- parts, start, end, unit;
-
- // will store per property easing and be used to determine when an animation is complete
- opt.animatedProperties = {};
-
- for ( p in prop ) {
-
- // property name normalization
- name = jQuery.camelCase( p );
- if ( p !== name ) {
- prop[ name ] = prop[ p ];
- delete prop[ p ];
- }
-
- val = prop[ name ];
-
- // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
- if ( jQuery.isArray( val ) ) {
- opt.animatedProperties[ name ] = val[ 1 ];
- val = prop[ name ] = val[ 0 ];
- } else {
- opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
- }
-
- if ( val === "hide" && hidden || val === "show" && !hidden ) {
- return opt.complete.call( this );
- }
-
- if ( isElement && ( name === "height" || name === "width" ) ) {
- // Make sure that nothing sneaks out
- // Record all 3 overflow attributes because IE does not
- // change the overflow attribute when overflowX and
- // overflowY are set to the same value
- opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
-
- // Set display property to inline-block for height/width
- // animations on inline elements that are having width/height
- // animated
- if ( jQuery.css( this, "display" ) === "inline" &&
- jQuery.css( this, "float" ) === "none" ) {
- if ( !jQuery.support.inlineBlockNeedsLayout ) {
- this.style.display = "inline-block";
-
- } else {
- display = defaultDisplay( this.nodeName );
-
- // inline-level elements accept inline-block;
- // block-level elements need to be inline with layout
- if ( display === "inline" ) {
- this.style.display = "inline-block";
-
- } else {
- this.style.display = "inline";
- this.style.zoom = 1;
- }
- }
- }
- }
- }
-
- if ( opt.overflow != null ) {
- this.style.overflow = "hidden";
- }
-
- for ( p in prop ) {
- e = new jQuery.fx( this, opt, p );
- val = prop[ p ];
-
- if ( rfxtypes.test(val) ) {
- e[ val === "toggle" ? hidden ? "show" : "hide" : val ]();
-
- } else {
- parts = rfxnum.exec( val );
- start = e.cur();
-
- if ( parts ) {
- end = parseFloat( parts[2] );
- unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
-
- // We need to compute starting value
- if ( unit !== "px" ) {
- jQuery.style( this, p, (end || 1) + unit);
- start = ((end || 1) / e.cur()) * start;
- jQuery.style( this, p, start + unit);
- }
-
- // If a +=/-= token was provided, we're doing a relative animation
- if ( parts[1] ) {
- end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
- }
-
- e.custom( start, end, unit );
-
- } else {
- e.custom( start, val, "" );
- }
- }
- }
-
- // For JS strict compliance
- return true;
- });
- },
-
- stop: function( clearQueue, gotoEnd ) {
- if ( clearQueue ) {
- this.queue([]);
- }
-
- this.each(function() {
- var timers = jQuery.timers,
- i = timers.length;
- // clear marker counters if we know they won't be
- if ( !gotoEnd ) {
- jQuery._unmark( true, this );
- }
- while ( i-- ) {
- if ( timers[i].elem === this ) {
- if (gotoEnd) {
- // force the next step to be the last
- timers[i](true);
- }
-
- timers.splice(i, 1);
- }
- }
- });
-
- // start the next in the queue if the last step wasn't forced
- if ( !gotoEnd ) {
- this.dequeue();
- }
-
- return this;
- }
-
-});
-
-// Animations created synchronously will run synchronously
-function createFxNow() {
- setTimeout( clearFxNow, 0 );
- return ( fxNow = jQuery.now() );
-}
-
-function clearFxNow() {
- fxNow = undefined;
-}
-
-// Generate parameters to create a standard animation
-function genFx( type, num ) {
- var obj = {};
-
- jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
- obj[ this ] = type;
- });
-
- return obj;
-}
-
-// Generate shortcuts for custom animations
-jQuery.each({
- slideDown: genFx("show", 1),
- slideUp: genFx("hide", 1),
- slideToggle: genFx("toggle", 1),
- fadeIn: { opacity: "show" },
- fadeOut: { opacity: "hide" },
- fadeToggle: { opacity: "toggle" }
-}, function( name, props ) {
- jQuery.fn[ name ] = function( speed, easing, callback ) {
- return this.animate( props, speed, easing, callback );
- };
-});
-
-jQuery.extend({
- speed: function( speed, easing, fn ) {
- var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
- complete: fn || !fn && easing ||
- jQuery.isFunction( speed ) && speed,
- duration: speed,
- easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
- };
-
- opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
- opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
-
- // Queueing
- opt.old = opt.complete;
- opt.complete = function( noUnmark ) {
- if ( jQuery.isFunction( opt.old ) ) {
- opt.old.call( this );
- }
-
- if ( opt.queue !== false ) {
- jQuery.dequeue( this );
- } else if ( noUnmark !== false ) {
- jQuery._unmark( this );
- }
- };
-
- return opt;
- },
-
- easing: {
- linear: function( p, n, firstNum, diff ) {
- return firstNum + diff * p;
- },
- swing: function( p, n, firstNum, diff ) {
- return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
- }
- },
-
- timers: [],
-
- fx: function( elem, options, prop ) {
- this.options = options;
- this.elem = elem;
- this.prop = prop;
-
- options.orig = options.orig || {};
- }
-
-});
-
-jQuery.fx.prototype = {
- // Simple function for setting a style value
- update: function() {
- if ( this.options.step ) {
- this.options.step.call( this.elem, this.now, this );
- }
-
- (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
- },
-
- // Get the current size
- cur: function() {
- if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
- return this.elem[ this.prop ];
- }
-
- var parsed,
- r = jQuery.css( this.elem, this.prop );
- // Empty strings, null, undefined and "auto" are converted to 0,
- // complex values such as "rotate(1rad)" are returned as is,
- // simple values such as "10px" are parsed to Float.
- return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
- },
-
- // Start an animation from one number to another
- custom: function( from, to, unit ) {
- var self = this,
- fx = jQuery.fx,
- raf;
-
- this.startTime = fxNow || createFxNow();
- this.start = from;
- this.end = to;
- this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
- this.now = this.start;
- this.pos = this.state = 0;
-
- function t( gotoEnd ) {
- return self.step(gotoEnd);
- }
-
- t.elem = this.elem;
-
- if ( t() && jQuery.timers.push(t) && !timerId ) {
- // Use requestAnimationFrame instead of setInterval if available
- if ( requestAnimationFrame ) {
- timerId = true;
- raf = function() {
- // When timerId gets set to null at any point, this stops
- if ( timerId ) {
- requestAnimationFrame( raf );
- fx.tick();
- }
- };
- requestAnimationFrame( raf );
- } else {
- timerId = setInterval( fx.tick, fx.interval );
- }
- }
- },
-
- // Simple 'show' function
- show: function() {
- // Remember where we started, so that we can go back to it later
- this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
- this.options.show = true;
-
- // Begin the animation
- // Make sure that we start at a small width/height to avoid any
- // flash of content
- this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
-
- // Start by showing the element
- jQuery( this.elem ).show();
- },
-
- // Simple 'hide' function
- hide: function() {
- // Remember where we started, so that we can go back to it later
- this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
- this.options.hide = true;
-
- // Begin the animation
- this.custom(this.cur(), 0);
- },
-
- // Each step of an animation
- step: function( gotoEnd ) {
- var t = fxNow || createFxNow(),
- done = true,
- elem = this.elem,
- options = this.options,
- i, n;
-
- if ( gotoEnd || t >= options.duration + this.startTime ) {
- this.now = this.end;
- this.pos = this.state = 1;
- this.update();
-
- options.animatedProperties[ this.prop ] = true;
-
- for ( i in options.animatedProperties ) {
- if ( options.animatedProperties[i] !== true ) {
- done = false;
- }
- }
-
- if ( done ) {
- // Reset the overflow
- if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
-
- jQuery.each( [ "", "X", "Y" ], function (index, value) {
- elem.style[ "overflow" + value ] = options.overflow[index];
- });
- }
-
- // Hide the element if the "hide" operation was done
- if ( options.hide ) {
- jQuery(elem).hide();
- }
-
- // Reset the properties, if the item has been hidden or shown
- if ( options.hide || options.show ) {
- for ( var p in options.animatedProperties ) {
- jQuery.style( elem, p, options.orig[p] );
- }
- }
-
- // Execute the complete function
- options.complete.call( elem );
- }
-
- return false;
-
- } else {
- // classical easing cannot be used with an Infinity duration
- if ( options.duration == Infinity ) {
- this.now = t;
- } else {
- n = t - this.startTime;
- this.state = n / options.duration;
-
- // Perform the easing function, defaults to swing
- this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration );
- this.now = this.start + ((this.end - this.start) * this.pos);
- }
- // Perform the next step of the animation
- this.update();
- }
-
- return true;
- }
-};
-
-jQuery.extend( jQuery.fx, {
- tick: function() {
- for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) {
- if ( !timers[i]() ) {
- timers.splice(i--, 1);
- }
- }
-
- if ( !timers.length ) {
- jQuery.fx.stop();
- }
- },
-
- interval: 13,
-
- stop: function() {
- clearInterval( timerId );
- timerId = null;
- },
-
- speeds: {
- slow: 600,
- fast: 200,
- // Default speed
- _default: 400
- },
-
- step: {
- opacity: function( fx ) {
- jQuery.style( fx.elem, "opacity", fx.now );
- },
-
- _default: function( fx ) {
- if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
- fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
- } else {
- fx.elem[ fx.prop ] = fx.now;
- }
- }
- }
-});
-
-if ( jQuery.expr && jQuery.expr.filters ) {
- jQuery.expr.filters.animated = function( elem ) {
- return jQuery.grep(jQuery.timers, function( fn ) {
- return elem === fn.elem;
- }).length;
- };
-}
-
-// Try to restore the default display value of an element
-function defaultDisplay( nodeName ) {
-
- if ( !elemdisplay[ nodeName ] ) {
-
- var body = document.body,
- elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
- display = elem.css( "display" );
-
- elem.remove();
-
- // If the simple way fails,
- // get element's real default display by attaching it to a temp iframe
- if ( display === "none" || display === "" ) {
- // No iframe to use yet, so create it
- if ( !iframe ) {
- iframe = document.createElement( "iframe" );
- iframe.frameBorder = iframe.width = iframe.height = 0;
- }
-
- body.appendChild( iframe );
-
- // Create a cacheable copy of the iframe document on first call.
- // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
- // document to it; WebKit & Firefox won't allow reusing the iframe document.
- if ( !iframeDoc || !iframe.createElement ) {
- iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
- iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "<!doctype html>" : "" ) + "<html><body>" );
- iframeDoc.close();
- }
-
- elem = iframeDoc.createElement( nodeName );
-
- iframeDoc.body.appendChild( elem );
-
- display = jQuery.css( elem, "display" );
-
- body.removeChild( iframe );
- }
-
- // Store the correct default display
- elemdisplay[ nodeName ] = display;
- }
-
- return elemdisplay[ nodeName ];
-}
-
-
-
-
-var rtable = /^t(?:able|d|h)$/i,
- rroot = /^(?:body|html)$/i;
-
-if ( "getBoundingClientRect" in document.documentElement ) {
- jQuery.fn.offset = function( options ) {
- var elem = this[0], box;
-
- if ( options ) {
- return this.each(function( i ) {
- jQuery.offset.setOffset( this, options, i );
- });
- }
-
- if ( !elem || !elem.ownerDocument ) {
- return null;
- }
-
- if ( elem === elem.ownerDocument.body ) {
- return jQuery.offset.bodyOffset( elem );
- }
-
- try {
- box = elem.getBoundingClientRect();
- } catch(e) {}
-
- var doc = elem.ownerDocument,
- docElem = doc.documentElement;
-
- // Make sure we're not dealing with a disconnected DOM node
- if ( !box || !jQuery.contains( docElem, elem ) ) {
- return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
- }
-
- var body = doc.body,
- win = getWindow(doc),
- clientTop = docElem.clientTop || body.clientTop || 0,
- clientLeft = docElem.clientLeft || body.clientLeft || 0,
- scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
- scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
- top = box.top + scrollTop - clientTop,
- left = box.left + scrollLeft - clientLeft;
-
- return { top: top, left: left };
- };
-
-} else {
- jQuery.fn.offset = function( options ) {
- var elem = this[0];
-
- if ( options ) {
- return this.each(function( i ) {
- jQuery.offset.setOffset( this, options, i );
- });
- }
-
- if ( !elem || !elem.ownerDocument ) {
- return null;
- }
-
- if ( elem === elem.ownerDocument.body ) {
- return jQuery.offset.bodyOffset( elem );
- }
-
- jQuery.offset.initialize();
-
- var computedStyle,
- offsetParent = elem.offsetParent,
- prevOffsetParent = elem,
- doc = elem.ownerDocument,
- docElem = doc.documentElement,
- body = doc.body,
- defaultView = doc.defaultView,
- prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
- top = elem.offsetTop,
- left = elem.offsetLeft;
-
- while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
- if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
- break;
- }
-
- computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
- top -= elem.scrollTop;
- left -= elem.scrollLeft;
-
- if ( elem === offsetParent ) {
- top += elem.offsetTop;
- left += elem.offsetLeft;
-
- if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
- top += parseFloat( computedStyle.borderTopWidth ) || 0;
- left += parseFloat( computedStyle.borderLeftWidth ) || 0;
- }
-
- prevOffsetParent = offsetParent;
- offsetParent = elem.offsetParent;
- }
-
- if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
- top += parseFloat( computedStyle.borderTopWidth ) || 0;
- left += parseFloat( computedStyle.borderLeftWidth ) || 0;
- }
-
- prevComputedStyle = computedStyle;
- }
-
- if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
- top += body.offsetTop;
- left += body.offsetLeft;
- }
-
- if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
- top += Math.max( docElem.scrollTop, body.scrollTop );
- left += Math.max( docElem.scrollLeft, body.scrollLeft );
- }
-
- return { top: top, left: left };
- };
-}
-
-jQuery.offset = {
- initialize: function() {
- var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
- html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
-
- jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
-
- container.innerHTML = html;
- body.insertBefore( container, body.firstChild );
- innerDiv = container.firstChild;
- checkDiv = innerDiv.firstChild;
- td = innerDiv.nextSibling.firstChild.firstChild;
-
- this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
- this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
-
- checkDiv.style.position = "fixed";
- checkDiv.style.top = "20px";
-
- // safari subtracts parent border width here which is 5px
- this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
- checkDiv.style.position = checkDiv.style.top = "";
-
- innerDiv.style.overflow = "hidden";
- innerDiv.style.position = "relative";
-
- this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
-
- this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
-
- body.removeChild( container );
- jQuery.offset.initialize = jQuery.noop;
- },
-
- bodyOffset: function( body ) {
- var top = body.offsetTop,
- left = body.offsetLeft;
-
- jQuery.offset.initialize();
-
- if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
- top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
- left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
- }
-
- return { top: top, left: left };
- },
-
- setOffset: function( elem, options, i ) {
- var position = jQuery.css( elem, "position" );
-
- // set position first, in-case top/left are set even on static elem
- if ( position === "static" ) {
- elem.style.position = "relative";
- }
-
- var curElem = jQuery( elem ),
- curOffset = curElem.offset(),
- curCSSTop = jQuery.css( elem, "top" ),
- curCSSLeft = jQuery.css( elem, "left" ),
- calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
- props = {}, curPosition = {}, curTop, curLeft;
-
- // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
- if ( calculatePosition ) {
- curPosition = curElem.position();
- curTop = curPosition.top;
- curLeft = curPosition.left;
- } else {
- curTop = parseFloat( curCSSTop ) || 0;
- curLeft = parseFloat( curCSSLeft ) || 0;
- }
-
- if ( jQuery.isFunction( options ) ) {
- options = options.call( elem, i, curOffset );
- }
-
- if (options.top != null) {
- props.top = (options.top - curOffset.top) + curTop;
- }
- if (options.left != null) {
- props.left = (options.left - curOffset.left) + curLeft;
- }
-
- if ( "using" in options ) {
- options.using.call( elem, props );
- } else {
- curElem.css( props );
- }
- }
-};
-
-
-jQuery.fn.extend({
- position: function() {
- if ( !this[0] ) {
- return null;
- }
-
- var elem = this[0],
-
- // Get *real* offsetParent
- offsetParent = this.offsetParent(),
-
- // Get correct offsets
- offset = this.offset(),
- parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
-
- // Subtract element margins
- // note: when an element has margin: auto the offsetLeft and marginLeft
- // are the same in Safari causing offset.left to incorrectly be 0
- offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
- offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
-
- // Add offsetParent borders
- parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
- parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
-
- // Subtract the two offsets
- return {
- top: offset.top - parentOffset.top,
- left: offset.left - parentOffset.left
- };
- },
-
- offsetParent: function() {
- return this.map(function() {
- var offsetParent = this.offsetParent || document.body;
- while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
- offsetParent = offsetParent.offsetParent;
- }
- return offsetParent;
- });
- }
-});
-
-
-// Create scrollLeft and scrollTop methods
-jQuery.each( ["Left", "Top"], function( i, name ) {
- var method = "scroll" + name;
-
- jQuery.fn[ method ] = function( val ) {
- var elem, win;
-
- if ( val === undefined ) {
- elem = this[ 0 ];
-
- if ( !elem ) {
- return null;
- }
-
- win = getWindow( elem );
-
- // Return the scroll offset
- return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
- jQuery.support.boxModel && win.document.documentElement[ method ] ||
- win.document.body[ method ] :
- elem[ method ];
- }
-
- // Set the scroll offset
- return this.each(function() {
- win = getWindow( this );
-
- if ( win ) {
- win.scrollTo(
- !i ? val : jQuery( win ).scrollLeft(),
- i ? val : jQuery( win ).scrollTop()
- );
-
- } else {
- this[ method ] = val;
- }
- });
- };
-});
-
-function getWindow( elem ) {
- return jQuery.isWindow( elem ) ?
- elem :
- elem.nodeType === 9 ?
- elem.defaultView || elem.parentWindow :
- false;
-}
-
-
-
-
-// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
-jQuery.each([ "Height", "Width" ], function( i, name ) {
-
- var type = name.toLowerCase();
-
- // innerHeight and innerWidth
- jQuery.fn[ "inner" + name ] = function() {
- var elem = this[0];
- return elem && elem.style ?
- parseFloat( jQuery.css( elem, type, "padding" ) ) :
- null;
- };
-
- // outerHeight and outerWidth
- jQuery.fn[ "outer" + name ] = function( margin ) {
- var elem = this[0];
- return elem && elem.style ?
- parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
- null;
- };
-
- jQuery.fn[ type ] = function( size ) {
- // Get window width or height
- var elem = this[0];
- if ( !elem ) {
- return size == null ? null : this;
- }
-
- if ( jQuery.isFunction( size ) ) {
- return this.each(function( i ) {
- var self = jQuery( this );
- self[ type ]( size.call( this, i, self[ type ]() ) );
- });
- }
-
- if ( jQuery.isWindow( elem ) ) {
- // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
- // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
- var docElemProp = elem.document.documentElement[ "client" + name ];
- return elem.document.compatMode === "CSS1Compat" && docElemProp ||
- elem.document.body[ "client" + name ] || docElemProp;
-
- // Get document width or height
- } else if ( elem.nodeType === 9 ) {
- // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
- return Math.max(
- elem.documentElement["client" + name],
- elem.body["scroll" + name], elem.documentElement["scroll" + name],
- elem.body["offset" + name], elem.documentElement["offset" + name]
- );
-
- // Get or set width or height on the element
- } else if ( size === undefined ) {
- var orig = jQuery.css( elem, type ),
- ret = parseFloat( orig );
-
- return jQuery.isNaN( ret ) ? orig : ret;
-
- // Set the width or height on the element (default to pixels if value is unitless)
- } else {
- return this.css( type, typeof size === "string" ? size : size + "px" );
- }
- };
-
-});
-
-
-// Expose jQuery to the global object
-window.jQuery = window.$ = jQuery;
-})(window);
+++ /dev/null
-
-body {
- font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
- padding: 60px 50px;
-}
-
-#mocha h1, h2 {
- margin: 0;
-}
-
-#mocha h1 {
- margin-top: 15px;
- font-size: 1em;
- font-weight: 200;
-}
-
-#mocha .suite .suite h1 {
- margin-top: 0;
- font-size: .8em;
-}
-
-#mocha h2 {
- font-size: 12px;
- font-weight: normal;
- cursor: pointer;
-}
-
-#mocha .suite {
- margin-left: 15px;
-}
-
-#mocha .test {
- margin-left: 15px;
-}
-
-#mocha .test:hover h2::after {
- position: relative;
- top: 0;
- right: -10px;
- content: '(view source)';
- font-size: 12px;
- font-family: arial;
- color: #888;
-}
-
-#mocha .test.pending:hover h2::after {
- content: '(pending)';
- font-family: arial;
-}
-
-#mocha .test.pass.medium .duration {
- background: #C09853;
-}
-
-#mocha .test.pass.slow .duration {
- background: #B94A48;
-}
-
-#mocha .test.pass::before {
- content: '✓';
- font-size: 12px;
- display: block;
- float: left;
- margin-right: 5px;
-}
-
-#mocha .test.pass .duration {
- font-size: 9px;
- margin-left: 5px;
- padding: 2px 5px;
- color: white;
- -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
- -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
- box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- -ms-border-radius: 5px;
- -o-border-radius: 5px;
- border-radius: 5px;
-}
-
-#mocha .test.pass.fast .duration {
- display: none;
-}
-
-#mocha .test.pending {
- color: #0b97c4;
-}
-
-#mocha .test.pending::before {
- content: '◦';
- color: #0b97c4;
-}
-
-#mocha .test.fail {
- color: #c00;
-}
-
-#mocha .test.fail pre {
- color: black;
-}
-
-#mocha .test.fail::before {
- content: '✖';
- font-size: 12px;
- display: block;
- float: left;
- margin-right: 5px;
- color: #c00;
-}
-
-#mocha .test pre.error {
- color: #c00;
-}
-
-#mocha .test pre {
- display: inline-block;
- font: 12px/1.5 monaco, monospace;
- margin: 5px;
- padding: 15px;
- border: 1px solid #eee;
- border-bottom-color: #ddd;
- -webkit-border-radius: 3px;
- -webkit-box-shadow: 0 1px 3px #eee;
-}
-
-#error {
- color: #c00;
- font-size: 1.5 em;
- font-weight: 100;
- letter-spacing: 1px;
-}
-
-#stats {
- position: fixed;
- top: 15px;
- right: 10px;
- font-size: 12px;
- margin: 0;
- color: #888;
-}
-
-#stats .progress {
- float: right;
- padding-top: 0;
-}
-
-#stats em {
- color: black;
-}
-
-#stats li {
- display: inline-block;
- margin: 0 5px;
- list-style: none;
- padding-top: 11px;
-}
-
-code .comment { color: #ddd }
-code .init { color: #2F6FAD }
-code .string { color: #5890AD }
-code .keyword { color: #8A6343 }
-code .number { color: #2F6FAD }
+++ /dev/null
-;(function(){
-
-
-// CommonJS require()
-
-function require(p){
- var path = require.resolve(p)
- , mod = require.modules[path];
- if (!mod) throw new Error('failed to require "' + p + '"');
- if (!mod.exports) {
- mod.exports = {};
- mod.call(mod.exports, mod, mod.exports, require.relative(path));
- }
- return mod.exports;
- }
-
-require.modules = {};
-
-require.resolve = function (path){
- var orig = path
- , reg = path + '.js'
- , index = path + '/index.js';
- return require.modules[reg] && reg
- || require.modules[index] && index
- || orig;
- };
-
-require.register = function (path, fn){
- require.modules[path] = fn;
- };
-
-require.relative = function (parent) {
- return function(p){
- if ('.' != p.charAt(0)) return require(p);
-
- var path = parent.split('/')
- , segs = p.split('/');
- path.pop();
-
- for (var i = 0; i < segs.length; i++) {
- var seg = segs[i];
- if ('..' == seg) path.pop();
- else if ('.' != seg) path.push(seg);
- }
-
- return require(path.join('/'));
- };
- };
-
-
-require.register("browser/debug.js", function(module, exports, require){
-
-module.exports = function(type){
- return function(){
-
- }
-};
-}); // module: browser/debug.js
-
-require.register("browser/diff.js", function(module, exports, require){
-
-}); // module: browser/diff.js
-
-require.register("browser/events.js", function(module, exports, require){
-
-/**
- * Module exports.
- */
-
-exports.EventEmitter = EventEmitter;
-
-/**
- * Check if `obj` is an array.
- */
-
-function isArray(obj) {
- return '[object Array]' == {}.toString.call(obj);
-}
-
-/**
- * Event emitter constructor.
- *
- * @api public.
- */
-
-function EventEmitter(){};
-
-/**
- * Adds a listener.
- *
- * @api public
- */
-
-EventEmitter.prototype.on = function (name, fn) {
- if (!this.$events) {
- this.$events = {};
- }
-
- if (!this.$events[name]) {
- this.$events[name] = fn;
- } else if (isArray(this.$events[name])) {
- this.$events[name].push(fn);
- } else {
- this.$events[name] = [this.$events[name], fn];
- }
-
- return this;
-};
-
-EventEmitter.prototype.addListener = EventEmitter.prototype.on;
-
-/**
- * Adds a volatile listener.
- *
- * @api public
- */
-
-EventEmitter.prototype.once = function (name, fn) {
- var self = this;
-
- function on () {
- self.removeListener(name, on);
- fn.apply(this, arguments);
- };
-
- on.listener = fn;
- this.on(name, on);
-
- return this;
-};
-
-/**
- * Removes a listener.
- *
- * @api public
- */
-
-EventEmitter.prototype.removeListener = function (name, fn) {
- if (this.$events && this.$events[name]) {
- var list = this.$events[name];
-
- if (isArray(list)) {
- var pos = -1;
-
- for (var i = 0, l = list.length; i < l; i++) {
- if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
- pos = i;
- break;
- }
- }
-
- if (pos < 0) {
- return this;
- }
-
- list.splice(pos, 1);
-
- if (!list.length) {
- delete this.$events[name];
- }
- } else if (list === fn || (list.listener && list.listener === fn)) {
- delete this.$events[name];
- }
- }
-
- return this;
-};
-
-/**
- * Removes all listeners for an event.
- *
- * @api public
- */
-
-EventEmitter.prototype.removeAllListeners = function (name) {
- if (name === undefined) {
- this.$events = {};
- return this;
- }
-
- if (this.$events && this.$events[name]) {
- this.$events[name] = null;
- }
-
- return this;
-};
-
-/**
- * Gets all listeners for a certain event.
- *
- * @api publci
- */
-
-EventEmitter.prototype.listeners = function (name) {
- if (!this.$events) {
- this.$events = {};
- }
-
- if (!this.$events[name]) {
- this.$events[name] = [];
- }
-
- if (!isArray(this.$events[name])) {
- this.$events[name] = [this.$events[name]];
- }
-
- return this.$events[name];
-};
-
-/**
- * Emits an event.
- *
- * @api public
- */
-
-EventEmitter.prototype.emit = function (name) {
- if (!this.$events) {
- return false;
- }
-
- var handler = this.$events[name];
-
- if (!handler) {
- return false;
- }
-
- var args = [].slice.call(arguments, 1);
-
- if ('function' == typeof handler) {
- handler.apply(this, args);
- } else if (isArray(handler)) {
- var listeners = handler.slice();
-
- for (var i = 0, l = listeners.length; i < l; i++) {
- listeners[i].apply(this, args);
- }
- } else {
- return false;
- }
-
- return true;
-};
-}); // module: browser/events.js
-
-require.register("browser/fs.js", function(module, exports, require){
-
-}); // module: browser/fs.js
-
-require.register("browser/path.js", function(module, exports, require){
-
-}); // module: browser/path.js
-
-require.register("browser/progress.js", function(module, exports, require){
-
-/**
- * Expose `Progress`.
- */
-
-module.exports = Progress;
-
-/**
- * Initialize a new `Progress` indicator.
- */
-
-function Progress() {
- this.percent = 0;
- this.size(0);
- this.fontSize(11);
- this.font('helvetica, arial, sans-serif');
-}
-
-/**
- * Set progress size to `n`.
- *
- * @param {Number} n
- * @return {Progress} for chaining
- * @api public
- */
-
-Progress.prototype.size = function(n){
- this._size = n;
- return this;
-};
-
-/**
- * Set text to `str`.
- *
- * @param {String} str
- * @return {Progress} for chaining
- * @api public
- */
-
-Progress.prototype.text = function(str){
- this._text = str;
- return this;
-};
-
-/**
- * Set font size to `n`.
- *
- * @param {Number} n
- * @return {Progress} for chaining
- * @api public
- */
-
-Progress.prototype.fontSize = function(n){
- this._fontSize = n;
- return this;
-};
-
-/**
- * Set font `family`.
- *
- * @param {String} family
- * @return {Progress} for chaining
- */
-
-Progress.prototype.font = function(family){
- this._font = family;
- return this;
-};
-
-/**
- * Update percentage to `n`.
- *
- * @param {Number} n
- * @return {Progress} for chaining
- */
-
-Progress.prototype.update = function(n){
- this.percent = n;
- return this;
-};
-
-/**
- * Draw on `ctx`.
- *
- * @param {CanvasRenderingContext2d} ctx
- * @return {Progress} for chaining
- */
-
-Progress.prototype.draw = function(ctx){
- var percent = Math.min(this.percent, 100)
- , size = this._size
- , half = size / 2
- , x = half
- , y = half
- , rad = half - 1
- , fontSize = this._fontSize;
-
- ctx.font = fontSize + 'px ' + this._font;
-
- var angle = Math.PI * 2 * (percent / 100);
- ctx.clearRect(0, 0, size, size);
-
- // outer circle
- ctx.strokeStyle = '#9f9f9f';
- ctx.beginPath();
- ctx.arc(x, y, rad, 0, angle, false);
- ctx.stroke();
-
- // inner circle
- ctx.strokeStyle = '#eee';
- ctx.beginPath();
- ctx.arc(x, y, rad - 1, 0, angle, true);
- ctx.stroke();
-
- // text
- var text = this._text || (percent | 0) + '%'
- , w = ctx.measureText(text).width;
-
- ctx.fillText(
- text
- , x - w / 2 + 1
- , y + fontSize / 2 - 1);
-
- return this;
-};
-
-}); // module: browser/progress.js
-
-require.register("browser/tty.js", function(module, exports, require){
-
-exports.isatty = function(){
- return true;
-};
-
-exports.getWindowSize = function(){
- return [window.innerHeight, window.innerWidth];
-};
-}); // module: browser/tty.js
-
-require.register("context.js", function(module, exports, require){
-
-/**
- * Expose `Context`.
- */
-
-module.exports = Context;
-
-/**
- * Initialize a new `Context`.
- *
- * @api private
- */
-
-function Context(){}
-
-/**
- * Set the context `Runnable` to `runnable`.
- *
- * @param {Runnable} runnable
- * @return {Context}
- * @api private
- */
-
-Context.prototype.runnable = function(runnable){
- this._runnable = runnable;
- return this;
-};
-
-/**
- * Set test timeout `ms`.
- *
- * @param {Number} ms
- * @return {Context} self
- * @api private
- */
-
-Context.prototype.timeout = function(ms){
- this._runnable.timeout(ms);
- return this;
-};
-
-/**
- * Inspect the context void of `._runnable`.
- *
- * @return {String}
- * @api private
- */
-
-Context.prototype.inspect = function(){
- return JSON.stringify(this, function(key, val){
- return '_runnable' == key
- ? undefined
- : val;
- }, 2);
-};
-
-}); // module: context.js
-
-require.register("hook.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Runnable = require('./runnable');
-
-/**
- * Expose `Hook`.
- */
-
-module.exports = Hook;
-
-/**
- * Initialize a new `Hook` with the given `title` and callback `fn`.
- *
- * @param {String} title
- * @param {Function} fn
- * @api private
- */
-
-function Hook(title, fn) {
- Runnable.call(this, title, fn);
- this.type = 'hook';
-}
-
-/**
- * Inherit from `Runnable.prototype`.
- */
-
-Hook.prototype = new Runnable;
-Hook.prototype.constructor = Hook;
-
-
-}); // module: hook.js
-
-require.register("interfaces/bdd.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Suite = require('../suite')
- , Test = require('../test');
-
-/**
- * BDD-style interface:
- *
- * describe('Array', function(){
- * describe('#indexOf()', function(){
- * it('should return -1 when not present', function(){
- *
- * });
- *
- * it('should return the index when present', function(){
- *
- * });
- * });
- * });
- *
- */
-
-module.exports = function(suite){
- var suites = [suite];
-
- suite.on('pre-require', function(context){
-
- // noop variants
-
- context.xdescribe = function(){};
- context.xit = function(){};
-
- /**
- * Execute before running tests.
- */
-
- context.before = function(fn){
- suites[0].beforeAll(fn);
- };
-
- /**
- * Execute after running tests.
- */
-
- context.after = function(fn){
- suites[0].afterAll(fn);
- };
-
- /**
- * Execute before each test case.
- */
-
- context.beforeEach = function(fn){
- suites[0].beforeEach(fn);
- };
-
- /**
- * Execute after each test case.
- */
-
- context.afterEach = function(fn){
- suites[0].afterEach(fn);
- };
-
- /**
- * Describe a "suite" with the given `title`
- * and callback `fn` containing nested suites
- * and/or tests.
- */
-
- context.describe = function(title, fn){
- var suite = Suite.create(suites[0], title);
- suites.unshift(suite);
- fn();
- suites.shift();
- };
-
- /**
- * Describe a specification or test-case
- * with the given `title` and callback `fn`
- * acting as a thunk.
- */
-
- context.it = function(title, fn){
- suites[0].addTest(new Test(title, fn));
- };
- });
-};
-
-}); // module: interfaces/bdd.js
-
-require.register("interfaces/exports.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Suite = require('../suite')
- , Test = require('../test');
-
-/**
- * TDD-style interface:
- *
- * exports.Array = {
- * '#indexOf()': {
- * 'should return -1 when the value is not present': function(){
- *
- * },
- *
- * 'should return the correct index when the value is present': function(){
- *
- * }
- * }
- * };
- *
- */
-
-module.exports = function(suite){
- var suites = [suite];
-
- suite.on('require', visit);
-
- function visit(obj) {
- var suite;
- for (var key in obj) {
- if ('function' == typeof obj[key]) {
- var fn = obj[key];
- switch (key) {
- case 'before':
- suites[0].beforeAll(fn);
- break;
- case 'after':
- suites[0].afterAll(fn);
- break;
- case 'beforeEach':
- suites[0].beforeEach(fn);
- break;
- case 'afterEach':
- suites[0].afterEach(fn);
- break;
- default:
- suites[0].addTest(new Test(key, fn));
- }
- } else {
- var suite = Suite.create(suites[0], key);
- suites.unshift(suite);
- visit(obj[key]);
- suites.shift();
- }
- }
- }
-};
-}); // module: interfaces/exports.js
-
-require.register("interfaces/index.js", function(module, exports, require){
-
-exports.bdd = require('./bdd');
-exports.tdd = require('./tdd');
-exports.qunit = require('./qunit');
-exports.exports = require('./exports');
-
-}); // module: interfaces/index.js
-
-require.register("interfaces/qunit.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Suite = require('../suite')
- , Test = require('../test');
-
-/**
- * QUnit-style interface:
- *
- * suite('Array');
- *
- * test('#length', function(){
- * var arr = [1,2,3];
- * ok(arr.length == 3);
- * });
- *
- * test('#indexOf()', function(){
- * var arr = [1,2,3];
- * ok(arr.indexOf(1) == 0);
- * ok(arr.indexOf(2) == 1);
- * ok(arr.indexOf(3) == 2);
- * });
- *
- * suite('String');
- *
- * test('#length', function(){
- * ok('foo'.length == 3);
- * });
- *
- */
-
-module.exports = function(suite){
- var suites = [suite];
-
- suite.on('pre-require', function(context){
-
- /**
- * Execute before running tests.
- */
-
- context.before = function(fn){
- suites[0].beforeAll(fn);
- };
-
- /**
- * Execute after running tests.
- */
-
- context.after = function(fn){
- suites[0].afterAll(fn);
- };
-
- /**
- * Execute before each test case.
- */
-
- context.beforeEach = function(fn){
- suites[0].beforeEach(fn);
- };
-
- /**
- * Execute after each test case.
- */
-
- context.afterEach = function(fn){
- suites[0].afterEach(fn);
- };
-
- /**
- * Describe a "suite" with the given `title`.
- */
-
- context.suite = function(title){
- if (suites.length > 1) suites.shift();
- var suite = Suite.create(suites[0], title);
- suites.unshift(suite);
- };
-
- /**
- * Describe a specification or test-case
- * with the given `title` and callback `fn`
- * acting as a thunk.
- */
-
- context.test = function(title, fn){
- suites[0].addTest(new Test(title, fn));
- };
- });
-};
-
-}); // module: interfaces/qunit.js
-
-require.register("interfaces/tdd.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Suite = require('../suite')
- , Test = require('../test');
-
-/**
- * TDD-style interface:
- *
- * suite('Array', function(){
- * suite('#indexOf()', function(){
- * suiteSetup(function(){
- *
- * });
- *
- * test('should return -1 when not present', function(){
- *
- * });
- *
- * test('should return the index when present', function(){
- *
- * });
- *
- * suiteTeardown(function(){
- *
- * });
- * });
- * });
- *
- */
-
-module.exports = function(suite){
- var suites = [suite];
-
- suite.on('pre-require', function(context){
-
- /**
- * Execute before each test case.
- */
-
- context.setup = function(fn){
- suites[0].beforeEach(fn);
- };
-
- /**
- * Execute after each test case.
- */
-
- context.teardown = function(fn){
- suites[0].afterEach(fn);
- };
-
- /**
- * Execute before the suite.
- */
-
- context.suiteSetup = function(fn){
- suites[0].beforeAll(fn);
- };
-
- /**
- * Execute after the suite.
- */
-
- context.suiteTeardown = function(fn){
- suites[0].afterAll(fn);
- };
-
- /**
- * Describe a "suite" with the given `title`
- * and callback `fn` containing nested suites
- * and/or tests.
- */
-
- context.suite = function(title, fn){
- var suite = Suite.create(suites[0], title);
- suites.unshift(suite);
- fn();
- suites.shift();
- };
-
- /**
- * Describe a specification or test-case
- * with the given `title` and callback `fn`
- * acting as a thunk.
- */
-
- context.test = function(title, fn){
- suites[0].addTest(new Test(title, fn));
- };
- });
-};
-
-}); // module: interfaces/tdd.js
-
-require.register("mocha.js", function(module, exports, require){
-
-/*!
- * mocha
- * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
- * MIT Licensed
- */
-
-/**
- * Module dependencies.
- */
-
-var path = require('browser/path');
-
-/**
- * Expose `Mocha`.
- */
-
-exports = module.exports = Mocha;
-
-/**
- * Library version.
- */
-
-exports.version = '1.0.3';
-
-/**
- * Expose internals.
- */
-
-exports.utils = require('./utils');
-exports.interfaces = require('./interfaces');
-exports.reporters = require('./reporters');
-exports.Runnable = require('./runnable');
-exports.Context = require('./context');
-exports.Runner = require('./runner');
-exports.Suite = require('./suite');
-exports.Hook = require('./hook');
-exports.Test = require('./test');
-
-/**
- * Return image `name` path.
- *
- * @param {String} name
- * @return {String}
- * @api private
- */
-
-function image(name) {
- return __dirname + '/../images/' + name + '.png';
-}
-
-/**
- * Setup mocha with `options`.
- *
- * Options:
- *
- * - `ui` name "bdd", "tdd", "exports" etc
- * - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
- * - `globals` array of accepted globals
- * - `timeout` timeout in milliseconds
- * - `ignoreLeaks` ignore global leaks
- * - `grep` string or regexp to filter tests with
- *
- * @param {Object} options
- * @api public
- */
-
-function Mocha(options) {
- options = options || {};
- options.grep = 'string' == typeof options.grep
- ? new RegExp(options.grep)
- : options.grep;
- this.files = [];
- this.options = options;
- this.suite = new exports.Suite('', new exports.Context);
- this.ui(options.ui);
- this.reporter(options.reporter);
- if (options.timeout) this.suite.timeout(options.timeout);
-}
-
-/**
- * Add test `file`.
- *
- * @param {String} file
- * @api public
- */
-
-Mocha.prototype.addFile = function(file){
- this.files.push(file);
- return this;
-};
-
-/**
- * Set reporter to `name`, defaults to "dot".
- *
- * @param {String} name
- * @api public
- */
-
-Mocha.prototype.reporter = function(name){
- name = name || 'dot';
- this._reporter = require('./reporters/' + name);
- if (!this._reporter) throw new Error('invalid reporter "' + name + '"');
- return this;
-};
-
-/**
- * Set test UI `name`, defaults to "bdd".
- *
- * @param {String} bdd
- * @api public
- */
-
-Mocha.prototype.ui = function(name){
- name = name || 'bdd';
- this._ui = exports.interfaces[name];
- if (!this._ui) throw new Error('invalid interface "' + name + '"');
- this._ui = this._ui(this.suite);
- return this;
-};
-
-/**
- * Load registered files.
- *
- * @api private
- */
-
-Mocha.prototype.loadFiles = function(){
- var suite = this.suite;
- this.files.forEach(function(file){
- file = path.resolve(file);
- suite.emit('pre-require', global, file);
- suite.emit('require', require(file), file);
- suite.emit('post-require', global, file);
- });
-};
-
-/**
- * Enable growl support.
- *
- * @api private
- */
-
-Mocha.prototype.growl = function(runner, reporter) {
- var notify = require('growl');
-
- runner.on('end', function(){
- var stats = reporter.stats;
- if (stats.failures) {
- var msg = stats.failures + ' of ' + runner.total + ' tests failed';
- notify(msg, { title: 'Failed', image: image('fail') });
- } else {
- notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
- title: 'Passed'
- , image: image('pass')
- });
- }
- });
-};
-
-/**
- * Add regexp to grep for to the options object
- *
- * @param {RegExp} or {String} re
- * @return {Mocha}
- * @api public
- */
-
-Mocha.prototype.grep = function(re){
- this.options.grep = 'string' == typeof re
- ? new RegExp(re)
- : re;
- return this;
-};
-
-/**
- * Run tests and invoke `fn()` when complete.
- *
- * @param {Function} fn
- * @return {Runner}
- * @api public
- */
-
-Mocha.prototype.run = function(fn){
- this.loadFiles();
- var suite = this.suite;
- var options = this.options;
- var runner = new exports.Runner(suite);
- var reporter = new this._reporter(runner);
- runner.ignoreLeaks = options.ignoreLeaks;
- if (options.grep) runner.grep(options.grep);
- if (options.globals) runner.globals(options.globals);
- if (options.growl) this.growl(runner, reporter);
- return runner.run(fn);
-};
-
-}); // module: mocha.js
-
-require.register("reporters/base.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var tty = require('browser/tty')
- , diff = require('browser/diff');
-
-/**
- * Save timer references to avoid Sinon interfering (see GH-237).
- */
-
-var Date = global.Date
- , setTimeout = global.setTimeout
- , setInterval = global.setInterval
- , clearTimeout = global.clearTimeout
- , clearInterval = global.clearInterval;
-
-/**
- * Check if both stdio streams are associated with a tty.
- */
-
-var isatty = tty.isatty(1) && tty.isatty(2);
-
-/**
- * Expose `Base`.
- */
-
-exports = module.exports = Base;
-
-/**
- * Enable coloring by default.
- */
-
-exports.useColors = isatty;
-
-/**
- * Default color map.
- */
-
-exports.colors = {
- 'pass': 90
- , 'fail': 31
- , 'bright pass': 92
- , 'bright fail': 91
- , 'bright yellow': 93
- , 'pending': 36
- , 'suite': 0
- , 'error title': 0
- , 'error message': 31
- , 'error stack': 90
- , 'checkmark': 32
- , 'fast': 90
- , 'medium': 33
- , 'slow': 31
- , 'green': 32
- , 'light': 90
- , 'diff gutter': 90
- , 'diff added': 42
- , 'diff removed': 41
-};
-
-/**
- * Color `str` with the given `type`,
- * allowing colors to be disabled,
- * as well as user-defined color
- * schemes.
- *
- * @param {String} type
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-var color = exports.color = function(type, str) {
- if (!exports.useColors) return str;
- return '\033[' + exports.colors[type] + 'm' + str + '\033[0m';
-};
-
-/**
- * Expose term window size, with some
- * defaults for when stderr is not a tty.
- */
-
-exports.window = {
- width: isatty
- ? process.stdout.getWindowSize
- ? process.stdout.getWindowSize(1)[0]
- : tty.getWindowSize()[1]
- : 75
-};
-
-/**
- * Expose some basic cursor interactions
- * that are common among reporters.
- */
-
-exports.cursor = {
- hide: function(){
- process.stdout.write('\033[?25l');
- },
-
- show: function(){
- process.stdout.write('\033[?25h');
- },
-
- deleteLine: function(){
- process.stdout.write('\033[2K');
- },
-
- beginningOfLine: function(){
- process.stdout.write('\033[0G');
- },
-
- CR: function(){
- exports.cursor.deleteLine();
- exports.cursor.beginningOfLine();
- }
-};
-
-/**
- * A test is considered slow if it
- * exceeds the following value in milliseconds.
- */
-
-exports.slow = 75;
-
-/**
- * Outut the given `failures` as a list.
- *
- * @param {Array} failures
- * @api public
- */
-
-exports.list = function(failures){
- console.error();
- failures.forEach(function(test, i){
- // format
- var fmt = color('error title', ' %s) %s:\n')
- + color('error message', ' %s')
- + color('error stack', '\n%s\n');
-
- // msg
- var err = test.err
- , message = err.message || ''
- , stack = err.stack || message
- , index = stack.indexOf(message) + message.length
- , msg = stack.slice(0, index)
- , actual = err.actual
- , expected = err.expected;
-
- // actual / expected diff
- if ('string' == typeof actual && 'string' == typeof expected) {
- var len = Math.max(actual.length, expected.length);
-
- if (len < 20) msg = errorDiff(err, 'Chars');
- else msg = errorDiff(err, 'Words');
-
- // linenos
- var lines = msg.split('\n');
- if (lines.length > 4) {
- var width = String(lines.length).length;
- msg = lines.map(function(str, i){
- return pad(++i, width) + ' |' + ' ' + str;
- }).join('\n');
- }
-
- // legend
- msg = '\n'
- + color('diff removed', 'actual')
- + ' '
- + color('diff added', 'expected')
- + '\n\n'
- + msg
- + '\n';
-
- // indent
- msg = msg.replace(/^/gm, ' ');
-
- fmt = color('error title', ' %s) %s:\n%s')
- + color('error stack', '\n%s\n');
- }
-
- // indent stack trace without msg
- stack = stack.slice(index ? index + 1 : index)
- .replace(/^/gm, ' ');
-
- console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
- });
-};
-
-/**
- * Initialize a new `Base` reporter.
- *
- * All other reporters generally
- * inherit from this reporter, providing
- * stats such as test duration, number
- * of tests passed / failed etc.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Base(runner) {
- var self = this
- , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }
- , failures = this.failures = [];
-
- if (!runner) return;
- this.runner = runner;
-
- runner.on('start', function(){
- stats.start = new Date;
- });
-
- runner.on('suite', function(suite){
- stats.suites = stats.suites || 0;
- suite.root || stats.suites++;
- });
-
- runner.on('test end', function(test){
- stats.tests = stats.tests || 0;
- stats.tests++;
- });
-
- runner.on('pass', function(test){
- stats.passes = stats.passes || 0;
-
- var medium = exports.slow / 2;
- test.speed = test.duration > exports.slow
- ? 'slow'
- : test.duration > medium
- ? 'medium'
- : 'fast';
-
- stats.passes++;
- });
-
- runner.on('fail', function(test, err){
- stats.failures = stats.failures || 0;
- stats.failures++;
- test.err = err;
- failures.push(test);
- });
-
- runner.on('end', function(){
- stats.end = new Date;
- stats.duration = new Date - stats.start;
- });
-
- runner.on('pending', function(){
- stats.pending++;
- });
-}
-
-/**
- * Output common epilogue used by many of
- * the bundled reporters.
- *
- * @api public
- */
-
-Base.prototype.epilogue = function(){
- var stats = this.stats
- , fmt
- , tests;
-
- console.log();
-
- function pluralize(n) {
- return 1 == n ? 'test' : 'tests';
- }
-
- // failure
- if (stats.failures) {
- fmt = color('bright fail', ' ✖')
- + color('fail', ' %d of %d %s failed')
- + color('light', ':')
-
- console.error(fmt,
- stats.failures,
- this.runner.total,
- pluralize(this.runner.total));
-
- Base.list(this.failures);
- console.error();
- return;
- }
-
- // pass
- fmt = color('bright pass', ' ✔')
- + color('green', ' %d %s complete')
- + color('light', ' (%dms)');
-
- console.log(fmt,
- stats.tests || 0,
- pluralize(stats.tests),
- stats.duration);
-
- // pending
- if (stats.pending) {
- fmt = color('pending', ' •')
- + color('pending', ' %d %s pending');
-
- console.log(fmt, stats.pending, pluralize(stats.pending));
- }
-
- console.log();
-};
-
-/**
- * Pad the given `str` to `len`.
- *
- * @param {String} str
- * @param {String} len
- * @return {String}
- * @api private
- */
-
-function pad(str, len) {
- str = String(str);
- return Array(len - str.length + 1).join(' ') + str;
-}
-
-/**
- * Return a character diff for `err`.
- *
- * @param {Error} err
- * @return {String}
- * @api private
- */
-
-function errorDiff(err, type) {
- return diff['diff' + type](err.actual, err.expected).map(function(str){
- if (/^(\n+)$/.test(str.value)) str.value = Array(++RegExp.$1.length).join('<newline>');
- if (str.added) return colorLines('diff added', str.value);
- if (str.removed) return colorLines('diff removed', str.value);
- return str.value;
- }).join('');
-}
-
-/**
- * Color lines for `str`, using the color `name`.
- *
- * @param {String} name
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-function colorLines(name, str) {
- return str.split('\n').map(function(str){
- return color(name, str);
- }).join('\n');
-}
-
-}); // module: reporters/base.js
-
-require.register("reporters/doc.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , utils = require('../utils');
-
-/**
- * Expose `Doc`.
- */
-
-exports = module.exports = Doc;
-
-/**
- * Initialize a new `Doc` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Doc(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , total = runner.total
- , indents = 2;
-
- function indent() {
- return Array(indents).join(' ');
- }
-
- runner.on('suite', function(suite){
- if (suite.root) return;
- ++indents;
- console.log('%s<section class="suite">', indent());
- ++indents;
- console.log('%s<h1>%s</h1>', indent(), suite.title);
- console.log('%s<dl>', indent());
- });
-
- runner.on('suite end', function(suite){
- if (suite.root) return;
- console.log('%s</dl>', indent());
- --indents;
- console.log('%s</section>', indent());
- --indents;
- });
-
- runner.on('pass', function(test){
- console.log('%s <dt>%s</dt>', indent(), test.title);
- var code = utils.escape(clean(test.fn.toString()));
- console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
- });
-}
-
-/**
- * Strip the function definition from `str`,
- * and re-indent for pre whitespace.
- */
-
-function clean(str) {
- str = str
- .replace(/^function *\(.*\) *{/, '')
- .replace(/\s+\}$/, '');
-
- var spaces = str.match(/^\n?( *)/)[1].length
- , re = new RegExp('^ {' + spaces + '}', 'gm');
-
- str = str.replace(re, '');
-
- return str;
-}
-}); // module: reporters/doc.js
-
-require.register("reporters/dot.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , color = Base.color;
-
-/**
- * Expose `Dot`.
- */
-
-exports = module.exports = Dot;
-
-/**
- * Initialize a new `Dot` matrix test reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Dot(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , width = Base.window.width * .75 | 0
- , n = 0;
-
- runner.on('start', function(){
- process.stdout.write('\n ');
- });
-
- runner.on('pending', function(test){
- process.stdout.write(color('pending', '.'));
- });
-
- runner.on('pass', function(test){
- if (++n % width == 0) process.stdout.write('\n ');
- if ('slow' == test.speed) {
- process.stdout.write(color('bright yellow', '.'));
- } else {
- process.stdout.write(color(test.speed, '.'));
- }
- });
-
- runner.on('fail', function(test, err){
- if (++n % width == 0) process.stdout.write('\n ');
- process.stdout.write(color('fail', '.'));
- });
-
- runner.on('end', function(){
- console.log();
- self.epilogue();
- });
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-Dot.prototype = new Base;
-Dot.prototype.constructor = Dot;
-
-}); // module: reporters/dot.js
-
-require.register("reporters/html-cov.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var JSONCov = require('./json-cov')
- , fs = require('browser/fs');
-
-/**
- * Expose `HTMLCov`.
- */
-
-exports = module.exports = HTMLCov;
-
-/**
- * Initialize a new `JsCoverage` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function HTMLCov(runner) {
- var jade = require('jade')
- , file = __dirname + '/templates/coverage.jade'
- , str = fs.readFileSync(file, 'utf8')
- , fn = jade.compile(str, { filename: file })
- , self = this;
-
- JSONCov.call(this, runner, false);
-
- runner.on('end', function(){
- process.stdout.write(fn({
- cov: self.cov
- , coverageClass: coverageClass
- }));
- });
-}
-
-function coverageClass(n) {
- if (n >= 75) return 'high';
- if (n >= 50) return 'medium';
- if (n >= 25) return 'low';
- return 'terrible';
-}
-}); // module: reporters/html-cov.js
-
-require.register("reporters/html.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , utils = require('../utils')
- , Progress = require('../browser/progress')
- , escape = utils.escape;
-
-/**
- * Save timer references to avoid Sinon interfering (see GH-237).
- */
-
-var Date = global.Date
- , setTimeout = global.setTimeout
- , setInterval = global.setInterval
- , clearTimeout = global.clearTimeout
- , clearInterval = global.clearInterval;
-
-/**
- * Expose `Doc`.
- */
-
-exports = module.exports = HTML;
-
-/**
- * Stats template.
- */
-
-var statsTemplate = '<ul id="stats">'
- + '<li class="progress"><canvas width="40" height="40"></canvas></li>'
- + '<li class="passes">passes: <em>0</em></li>'
- + '<li class="failures">failures: <em>0</em></li>'
- + '<li class="duration">duration: <em>0</em>s</li>'
- + '</ul>';
-
-/**
- * Initialize a new `Doc` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function HTML(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , total = runner.total
- , root = document.getElementById('mocha')
- , stat = fragment(statsTemplate)
- , items = stat.getElementsByTagName('li')
- , passes = items[1].getElementsByTagName('em')[0]
- , failures = items[2].getElementsByTagName('em')[0]
- , duration = items[3].getElementsByTagName('em')[0]
- , canvas = stat.getElementsByTagName('canvas')[0]
- , stack = [root]
- , progress
- , ctx
-
- if (canvas.getContext) {
- ctx = canvas.getContext('2d');
- progress = new Progress;
- }
-
- if (!root) return error('#mocha div missing, add it to your document');
-
- root.appendChild(stat);
-
- if (progress) progress.size(40);
-
- runner.on('suite', function(suite){
- if (suite.root) return;
-
- // suite
- var el = fragment('<div class="suite"><h1>%s</h1></div>', suite.title);
-
- // container
- stack[0].appendChild(el);
- stack.unshift(document.createElement('div'));
- el.appendChild(stack[0]);
- });
-
- runner.on('suite end', function(suite){
- if (suite.root) return;
- stack.shift();
- });
-
- runner.on('fail', function(test, err){
- if ('hook' == test.type || err.uncaught) runner.emit('test end', test);
- });
-
- runner.on('test end', function(test){
- // TODO: add to stats
- var percent = stats.tests / total * 100 | 0;
- if (progress) progress.update(percent).draw(ctx);
-
- // update stats
- var ms = new Date - stats.start;
- text(passes, stats.passes);
- text(failures, stats.failures);
- text(duration, (ms / 1000).toFixed(2));
-
- // test
- if ('passed' == test.state) {
- var el = fragment('<div class="test pass %e"><h2>%e<span class="duration">%ems</span></h2></div>', test.speed, test.title, test.duration);
- } else if (test.pending) {
- var el = fragment('<div class="test pass pending"><h2>%e</h2></div>', test.title);
- } else {
- var el = fragment('<div class="test fail"><h2>%e</h2></div>', test.title);
- var str = test.err.stack || test.err.toString();
-
- // FF / Opera do not add the message
- if (!~str.indexOf(test.err.message)) {
- str = test.err.message + '\n' + str;
- }
-
- // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
- // check for the result of the stringifying.
- if ('[object Error]' == str) str = test.err.message;
-
- // Safari doesn't give you a stack. Let's at least provide a source line.
- if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) {
- str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")";
- }
-
- el.appendChild(fragment('<pre class="error">%e</pre>', str));
- }
-
- // toggle code
- var h2 = el.getElementsByTagName('h2')[0];
-
- on(h2, 'click', function(){
- pre.style.display = 'none' == pre.style.display
- ? 'block'
- : 'none';
- });
-
- // code
- // TODO: defer
- if (!test.pending) {
- var pre = fragment('<pre><code>%e</code></pre>', clean(test.fn.toString()));
- el.appendChild(pre);
- pre.style.display = 'none';
- }
-
- stack[0].appendChild(el);
- });
-}
-
-/**
- * Display error `msg`.
- */
-
-function error(msg) {
- document.body.appendChild(fragment('<div id="error">%s</div>', msg));
-}
-
-/**
- * Return a DOM fragment from `html`.
- */
-
-function fragment(html) {
- var args = arguments
- , div = document.createElement('div')
- , i = 1;
-
- div.innerHTML = html.replace(/%([se])/g, function(_, type){
- switch (type) {
- case 's': return String(args[i++]);
- case 'e': return escape(args[i++]);
- }
- });
-
- return div.firstChild;
-}
-
-/**
- * Set `el` text to `str`.
- */
-
-function text(el, str) {
- if (el.textContent) {
- el.textContent = str;
- } else {
- el.innerText = str;
- }
-}
-
-/**
- * Listen on `event` with callback `fn`.
- */
-
-function on(el, event, fn) {
- if (el.addEventListener) {
- el.addEventListener(event, fn, false);
- } else {
- el.attachEvent('on' + event, fn);
- }
-}
-
-/**
- * Strip the function definition from `str`,
- * and re-indent for pre whitespace.
- */
-
-function clean(str) {
- str = str
- .replace(/^function *\(.*\) *{/, '')
- .replace(/\s+\}$/, '');
-
- var spaces = str.match(/^\n?( *)/)[1].length
- , re = new RegExp('^ {' + spaces + '}', 'gm');
-
- str = str
- .replace(re, '')
- .replace(/^\s+/, '');
-
- return str;
-}
-
-}); // module: reporters/html.js
-
-require.register("reporters/index.js", function(module, exports, require){
-
-exports.Base = require('./base');
-exports.Dot = require('./dot');
-exports.Doc = require('./doc');
-exports.TAP = require('./tap');
-exports.JSON = require('./json');
-exports.HTML = require('./html');
-exports.List = require('./list');
-exports.Min = require('./min');
-exports.Spec = require('./spec');
-exports.Progress = require('./progress');
-exports.Landing = require('./landing');
-exports.JSONCov = require('./json-cov');
-exports.HTMLCov = require('./html-cov');
-exports.JSONStream = require('./json-stream');
-exports.XUnit = require('./xunit')
-exports.Teamcity = require('./teamcity')
-
-}); // module: reporters/index.js
-
-require.register("reporters/json-cov.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base');
-
-/**
- * Expose `JSONCov`.
- */
-
-exports = module.exports = JSONCov;
-
-/**
- * Initialize a new `JsCoverage` reporter.
- *
- * @param {Runner} runner
- * @param {Boolean} output
- * @api public
- */
-
-function JSONCov(runner, output) {
- var self = this
- , output = 1 == arguments.length ? true : output;
-
- Base.call(this, runner);
-
- var tests = []
- , failures = []
- , passes = [];
-
- runner.on('test end', function(test){
- tests.push(test);
- });
-
- runner.on('pass', function(test){
- passes.push(test);
- });
-
- runner.on('fail', function(test){
- failures.push(test);
- });
-
- runner.on('end', function(){
- var cov = global._$jscoverage || {};
- var result = self.cov = map(cov);
- result.stats = self.stats;
- result.tests = tests.map(clean);
- result.failures = failures.map(clean);
- result.passes = passes.map(clean);
- if (!output) return;
- process.stdout.write(JSON.stringify(result, null, 2 ));
- });
-}
-
-/**
- * Map jscoverage data to a JSON structure
- * suitable for reporting.
- *
- * @param {Object} cov
- * @return {Object}
- * @api private
- */
-
-function map(cov) {
- var ret = {
- instrumentation: 'node-jscoverage'
- , sloc: 0
- , hits: 0
- , misses: 0
- , coverage: 0
- , files: []
- };
-
- for (var filename in cov) {
- var data = coverage(filename, cov[filename]);
- ret.files.push(data);
- ret.hits += data.hits;
- ret.misses += data.misses;
- ret.sloc += data.sloc;
- }
-
- if (ret.sloc > 0) {
- ret.coverage = (ret.hits / ret.sloc) * 100;
- }
-
- return ret;
-};
-
-/**
- * Map jscoverage data for a single source file
- * to a JSON structure suitable for reporting.
- *
- * @param {String} filename name of the source file
- * @param {Object} data jscoverage coverage data
- * @return {Object}
- * @api private
- */
-
-function coverage(filename, data) {
- var ret = {
- filename: filename,
- coverage: 0,
- hits: 0,
- misses: 0,
- sloc: 0,
- source: {}
- };
-
- data.source.forEach(function(line, num){
- num++;
-
- if (data[num] === 0) {
- ret.misses++;
- ret.sloc++;
- } else if (data[num] !== undefined) {
- ret.hits++;
- ret.sloc++;
- }
-
- ret.source[num] = {
- source: line
- , coverage: data[num] === undefined
- ? ''
- : data[num]
- };
- });
-
- ret.coverage = ret.hits / ret.sloc * 100;
-
- return ret;
-}
-
-/**
- * Return a plain-object representation of `test`
- * free of cyclic properties etc.
- *
- * @param {Object} test
- * @return {Object}
- * @api private
- */
-
-function clean(test) {
- return {
- title: test.title
- , fullTitle: test.fullTitle()
- , duration: test.duration
- }
-}
-
-}); // module: reporters/json-cov.js
-
-require.register("reporters/json-stream.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , color = Base.color;
-
-/**
- * Expose `List`.
- */
-
-exports = module.exports = List;
-
-/**
- * Initialize a new `List` test reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function List(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , total = runner.total;
-
- runner.on('start', function(){
- console.log(JSON.stringify(['start', { total: total }]));
- });
-
- runner.on('pass', function(test){
- console.log(JSON.stringify(['pass', clean(test)]));
- });
-
- runner.on('fail', function(test, err){
- console.log(JSON.stringify(['fail', clean(test)]));
- });
-
- runner.on('end', function(){
- process.stdout.write(JSON.stringify(['end', self.stats]));
- });
-}
-
-/**
- * Return a plain-object representation of `test`
- * free of cyclic properties etc.
- *
- * @param {Object} test
- * @return {Object}
- * @api private
- */
-
-function clean(test) {
- return {
- title: test.title
- , fullTitle: test.fullTitle()
- , duration: test.duration
- }
-}
-}); // module: reporters/json-stream.js
-
-require.register("reporters/json.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `JSON`.
- */
-
-exports = module.exports = JSONReporter;
-
-/**
- * Initialize a new `JSON` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function JSONReporter(runner) {
- var self = this;
- Base.call(this, runner);
-
- var tests = []
- , failures = []
- , passes = [];
-
- runner.on('test end', function(test){
- tests.push(test);
- });
-
- runner.on('pass', function(test){
- passes.push(test);
- });
-
- runner.on('fail', function(test){
- failures.push(test);
- });
-
- runner.on('end', function(){
- var obj = {
- stats: self.stats
- , tests: tests.map(clean)
- , failures: failures.map(clean)
- , passes: passes.map(clean)
- };
-
- process.stdout.write(JSON.stringify(obj, null, 2));
- });
-}
-
-/**
- * Return a plain-object representation of `test`
- * free of cyclic properties etc.
- *
- * @param {Object} test
- * @return {Object}
- * @api private
- */
-
-function clean(test) {
- return {
- title: test.title
- , fullTitle: test.fullTitle()
- , duration: test.duration
- }
-}
-}); // module: reporters/json.js
-
-require.register("reporters/landing.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `Landing`.
- */
-
-exports = module.exports = Landing;
-
-/**
- * Airplane color.
- */
-
-Base.colors.plane = 0;
-
-/**
- * Airplane crash color.
- */
-
-Base.colors['plane crash'] = 31;
-
-/**
- * Runway color.
- */
-
-Base.colors.runway = 90;
-
-/**
- * Initialize a new `Landing` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Landing(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , width = Base.window.width * .75 | 0
- , total = runner.total
- , stream = process.stdout
- , plane = color('plane', '✈')
- , crashed = -1
- , n = 0;
-
- function runway() {
- var buf = Array(width).join('-');
- return ' ' + color('runway', buf);
- }
-
- runner.on('start', function(){
- stream.write('\n ');
- cursor.hide();
- });
-
- runner.on('test end', function(test){
- // check if the plane crashed
- var col = -1 == crashed
- ? width * ++n / total | 0
- : crashed;
-
- // show the crash
- if ('failed' == test.state) {
- plane = color('plane crash', '✈');
- crashed = col;
- }
-
- // render landing strip
- stream.write('\033[4F\n\n');
- stream.write(runway());
- stream.write('\n ');
- stream.write(color('runway', Array(col).join('⋅')));
- stream.write(plane)
- stream.write(color('runway', Array(width - col).join('⋅') + '\n'));
- stream.write(runway());
- stream.write('\033[0m');
- });
-
- runner.on('end', function(){
- cursor.show();
- console.log();
- self.epilogue();
- });
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-Landing.prototype = new Base;
-Landing.prototype.constructor = Landing;
-
-}); // module: reporters/landing.js
-
-require.register("reporters/list.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `List`.
- */
-
-exports = module.exports = List;
-
-/**
- * Initialize a new `List` test reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function List(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , n = 0;
-
- runner.on('start', function(){
- console.log();
- });
-
- runner.on('test', function(test){
- process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
- });
-
- runner.on('pending', function(test){
- var fmt = color('checkmark', ' -')
- + color('pending', ' %s');
- console.log(fmt, test.fullTitle());
- });
-
- runner.on('pass', function(test){
- var fmt = color('checkmark', ' ✓')
- + color('pass', ' %s: ')
- + color(test.speed, '%dms');
- cursor.CR();
- console.log(fmt, test.fullTitle(), test.duration);
- });
-
- runner.on('fail', function(test, err){
- cursor.CR();
- console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
- });
-
- runner.on('end', self.epilogue.bind(self));
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-List.prototype = new Base;
-List.prototype.constructor = List;
-
-
-}); // module: reporters/list.js
-
-require.register("reporters/markdown.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , utils = require('../utils');
-
-/**
- * Expose `Markdown`.
- */
-
-exports = module.exports = Markdown;
-
-/**
- * Initialize a new `Markdown` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Markdown(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , total = runner.total
- , level = 0
- , buf = '';
-
- function title(str) {
- return Array(level).join('#') + ' ' + str;
- }
-
- function indent() {
- return Array(level).join(' ');
- }
-
- function mapTOC(suite, obj) {
- var ret = obj;
- obj = obj[suite.title] = obj[suite.title] || { suite: suite };
- suite.suites.forEach(function(suite){
- mapTOC(suite, obj);
- });
- return ret;
- }
-
- function stringifyTOC(obj, level) {
- ++level;
- var buf = '';
- var link;
- for (var key in obj) {
- if ('suite' == key) continue;
- if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
- if (key) buf += Array(level).join(' ') + link;
- buf += stringifyTOC(obj[key], level);
- }
- --level;
- return buf;
- }
-
- function generateTOC(suite) {
- var obj = mapTOC(suite, {});
- return stringifyTOC(obj, 0);
- }
-
- generateTOC(runner.suite);
-
- runner.on('suite', function(suite){
- ++level;
- var slug = utils.slug(suite.fullTitle());
- buf += '<a name="' + slug + '" />' + '\n';
- buf += title(suite.title) + '\n';
- });
-
- runner.on('suite end', function(suite){
- --level;
- });
-
- runner.on('pass', function(test){
- var code = clean(test.fn.toString());
- buf += test.title + '.\n';
- buf += '\n```js';
- buf += code + '\n';
- buf += '```\n\n';
- });
-
- runner.on('end', function(){
- process.stdout.write('# TOC\n');
- process.stdout.write(generateTOC(runner.suite));
- process.stdout.write(buf);
- });
-}
-
-/**
- * Strip the function definition from `str`,
- * and re-indent for pre whitespace.
- */
-
-function clean(str) {
- str = str
- .replace(/^function *\(.*\) *{/, '')
- .replace(/\s+\}$/, '');
-
- var spaces = str.match(/^\n?( *)/)[1].length
- , re = new RegExp('^ {' + spaces + '}', 'gm');
-
- str = str.replace(re, '');
-
- return str;
-}
-}); // module: reporters/markdown.js
-
-require.register("reporters/min.js", function(module, exports, require){
-/**
- * Module dependencies.
- */
-
-var Base = require('./base');
-
-/**
- * Expose `Min`.
- */
-
-exports = module.exports = Min;
-
-/**
- * Initialize a new `Min` minimal test reporter (best used with --watch).
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Min(runner) {
- Base.call(this, runner);
-
- runner.on('start', function(){
- // clear screen
- process.stdout.write('\033[2J');
- // set cursor position
- process.stdout.write('\033[1;3H');
- });
-
- runner.on('end', this.epilogue.bind(this));
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-Min.prototype = new Base;
-Min.prototype.constructor = Min;
-
-}); // module: reporters/min.js
-
-require.register("reporters/progress.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `Progress`.
- */
-
-exports = module.exports = Progress;
-
-/**
- * General progress bar color.
- */
-
-Base.colors.progress = 90;
-
-/**
- * Initialize a new `Progress` bar test reporter.
- *
- * @param {Runner} runner
- * @param {Object} options
- * @api public
- */
-
-function Progress(runner, options) {
- Base.call(this, runner);
-
- var self = this
- , options = options || {}
- , stats = this.stats
- , width = Base.window.width * .50 | 0
- , total = runner.total
- , complete = 0
- , max = Math.max;
-
- // default chars
- options.open = options.open || '[';
- options.complete = options.complete || '▬';
- options.incomplete = options.incomplete || '⋅';
- options.close = options.close || ']';
- options.verbose = false;
-
- // tests started
- runner.on('start', function(){
- console.log();
- cursor.hide();
- });
-
- // tests complete
- runner.on('test end', function(){
- complete++;
- var incomplete = total - complete
- , percent = complete / total
- , n = width * percent | 0
- , i = width - n;
-
- cursor.CR();
- process.stdout.write('\033[J');
- process.stdout.write(color('progress', ' ' + options.open));
- process.stdout.write(Array(n).join(options.complete));
- process.stdout.write(Array(i).join(options.incomplete));
- process.stdout.write(color('progress', options.close));
- if (options.verbose) {
- process.stdout.write(color('progress', ' ' + complete + ' of ' + total));
- }
- });
-
- // tests are complete, output some stats
- // and the failures if any
- runner.on('end', function(){
- cursor.show();
- console.log();
- self.epilogue();
- });
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-Progress.prototype = new Base;
-Progress.prototype.constructor = Progress;
-
-
-}); // module: reporters/progress.js
-
-require.register("reporters/spec.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `Spec`.
- */
-
-exports = module.exports = Spec;
-
-/**
- * Initialize a new `Spec` test reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Spec(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , indents = 0
- , n = 0;
-
- function indent() {
- return Array(indents).join(' ')
- }
-
- runner.on('start', function(){
- console.log();
- });
-
- runner.on('suite', function(suite){
- ++indents;
- console.log(color('suite', '%s%s'), indent(), suite.title);
- });
-
- runner.on('suite end', function(suite){
- --indents;
- if (1 == indents) console.log();
- });
-
- runner.on('test', function(test){
- process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': '));
- });
-
- runner.on('pending', function(test){
- var fmt = indent() + color('pending', ' - %s');
- console.log(fmt, test.title);
- });
-
- runner.on('pass', function(test){
- if ('fast' == test.speed) {
- var fmt = indent()
- + color('checkmark', ' ✓')
- + color('pass', ' %s ');
- cursor.CR();
- console.log(fmt, test.title);
- } else {
- var fmt = indent()
- + color('checkmark', ' ✓')
- + color('pass', ' %s ')
- + color(test.speed, '(%dms)');
- cursor.CR();
- console.log(fmt, test.title, test.duration);
- }
- });
-
- runner.on('fail', function(test, err){
- cursor.CR();
- console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
- });
-
- runner.on('end', self.epilogue.bind(self));
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-Spec.prototype = new Base;
-Spec.prototype.constructor = Spec;
-
-
-}); // module: reporters/spec.js
-
-require.register("reporters/tap.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , cursor = Base.cursor
- , color = Base.color;
-
-/**
- * Expose `TAP`.
- */
-
-exports = module.exports = TAP;
-
-/**
- * Initialize a new `TAP` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function TAP(runner) {
- Base.call(this, runner);
-
- var self = this
- , stats = this.stats
- , total = runner.total
- , n = 1;
-
- runner.on('start', function(){
- console.log('%d..%d', 1, total);
- });
-
- runner.on('test end', function(){
- ++n;
- });
-
- runner.on('pending', function(test){
- console.log('ok %d %s # SKIP -', n, title(test));
- });
-
- runner.on('pass', function(test){
- console.log('ok %d %s', n, title(test));
- });
-
- runner.on('fail', function(test, err){
- console.log('not ok %d %s', n, title(test));
- console.log(err.stack.replace(/^/gm, ' '));
- });
-}
-
-/**
- * Return a TAP-safe title of `test`
- *
- * @param {Object} test
- * @return {String}
- * @api private
- */
-
-function title(test) {
- return test.fullTitle().replace(/#/g, '');
-}
-
-}); // module: reporters/tap.js
-
-require.register("reporters/teamcity.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base');
-
-/**
- * Expose `Teamcity`.
- */
-
-exports = module.exports = Teamcity;
-
-/**
- * Initialize a new `Teamcity` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function Teamcity(runner) {
- Base.call(this, runner);
- var stats = this.stats;
-
- runner.on('start', function() {
- console.log("##teamcity[testSuiteStarted name='mocha.suite']");
- });
-
- runner.on('test', function(test) {
- console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']");
- });
-
- runner.on('fail', function(test, err) {
- console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']");
- });
-
- runner.on('pending', function(test) {
- console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']");
- });
-
- runner.on('test end', function(test) {
- console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']");
- });
-
- runner.on('end', function() {
- console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']");
- });
-}
-
-/**
- * Escape the given `str`.
- */
-
-function escape(str) {
- return str.replace(/'/g, "|'");
-}
-}); // module: reporters/teamcity.js
-
-require.register("reporters/xunit.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Base = require('./base')
- , utils = require('../utils')
- , escape = utils.escape;
-
-/**
- * Save timer references to avoid Sinon interfering (see GH-237).
- */
-
-var Date = global.Date
- , setTimeout = global.setTimeout
- , setInterval = global.setInterval
- , clearTimeout = global.clearTimeout
- , clearInterval = global.clearInterval;
-
-/**
- * Expose `XUnit`.
- */
-
-exports = module.exports = XUnit;
-
-/**
- * Initialize a new `XUnit` reporter.
- *
- * @param {Runner} runner
- * @api public
- */
-
-function XUnit(runner) {
- Base.call(this, runner);
- var stats = this.stats
- , tests = []
- , self = this;
-
- runner.on('test end', function(test){
- tests.push(test);
- });
-
- runner.on('end', function(){
- console.log(tag('testsuite', {
- name: 'Mocha Tests'
- , tests: stats.tests
- , failures: stats.failures
- , errors: stats.failures
- , skip: stats.tests - stats.failures - stats.passes
- , timestamp: (new Date).toUTCString()
- , time: stats.duration / 1000
- }, false));
-
- tests.forEach(test);
- console.log('</testsuite>');
- });
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-
-XUnit.prototype = new Base;
-XUnit.prototype.constructor = XUnit;
-
-
-/**
- * Output tag for the given `test.`
- */
-
-function test(test) {
- var attrs = {
- classname: test.parent.fullTitle()
- , name: test.title
- , time: test.duration / 1000
- };
-
- if ('failed' == test.state) {
- var err = test.err;
- attrs.message = escape(err.message);
- console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
- } else if (test.pending) {
- console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
- } else {
- console.log(tag('testcase', attrs, true) );
- }
-}
-
-/**
- * HTML tag helper.
- */
-
-function tag(name, attrs, close, content) {
- var end = close ? '/>' : '>'
- , pairs = []
- , tag;
-
- for (var key in attrs) {
- pairs.push(key + '="' + escape(attrs[key]) + '"');
- }
-
- tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
- if (content) tag += content + '</' + name + end;
- return tag;
-}
-
-/**
- * Return cdata escaped CDATA `str`.
- */
-
-function cdata(str) {
- return '<![CDATA[' + escape(str) + ']]>';
-}
-
-}); // module: reporters/xunit.js
-
-require.register("runnable.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var EventEmitter = require('browser/events').EventEmitter
- , debug = require('browser/debug')('runnable');
-
-/**
- * Save timer references to avoid Sinon interfering (see GH-237).
- */
-
-var Date = global.Date
- , setTimeout = global.setTimeout
- , setInterval = global.setInterval
- , clearTimeout = global.clearTimeout
- , clearInterval = global.clearInterval;
-
-/**
- * Expose `Runnable`.
- */
-
-module.exports = Runnable;
-
-/**
- * Initialize a new `Runnable` with the given `title` and callback `fn`.
- *
- * @param {String} title
- * @param {Function} fn
- * @api private
- */
-
-function Runnable(title, fn) {
- this.title = title;
- this.fn = fn;
- this.async = fn && fn.length;
- this.sync = ! this.async;
- this._timeout = 2000;
- this.timedOut = false;
-}
-
-/**
- * Inherit from `EventEmitter.prototype`.
- */
-
-Runnable.prototype = new EventEmitter;
-Runnable.prototype.constructor = Runnable;
-
-
-/**
- * Set & get timeout `ms`.
- *
- * @param {Number} ms
- * @return {Runnable|Number} ms or self
- * @api private
- */
-
-Runnable.prototype.timeout = function(ms){
- if (0 == arguments.length) return this._timeout;
- debug('timeout %d', ms);
- this._timeout = ms;
- if (this.timer) this.resetTimeout();
- return this;
-};
-
-/**
- * Return the full title generated by recursively
- * concatenating the parent's full title.
- *
- * @return {String}
- * @api public
- */
-
-Runnable.prototype.fullTitle = function(){
- return this.parent.fullTitle() + ' ' + this.title;
-};
-
-/**
- * Clear the timeout.
- *
- * @api private
- */
-
-Runnable.prototype.clearTimeout = function(){
- clearTimeout(this.timer);
-};
-
-/**
- * Reset the timeout.
- *
- * @api private
- */
-
-Runnable.prototype.resetTimeout = function(){
- var self = this
- , ms = this.timeout();
-
- this.clearTimeout();
- if (ms) {
- this.timer = setTimeout(function(){
- self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
- self.timedOut = true;
- }, ms);
- }
-};
-
-/**
- * Run the test and invoke `fn(err)`.
- *
- * @param {Function} fn
- * @api private
- */
-
-Runnable.prototype.run = function(fn){
- var self = this
- , ms = this.timeout()
- , start = new Date
- , ctx = this.ctx
- , finished
- , emitted;
-
- if (ctx) ctx.runnable(this);
-
- // timeout
- if (this.async) {
- if (ms) {
- this.timer = setTimeout(function(){
- done(new Error('timeout of ' + ms + 'ms exceeded'));
- self.timedOut = true;
- }, ms);
- }
- }
-
- // called multiple times
- function multiple() {
- if (emitted) return;
- emitted = true;
- self.emit('error', new Error('done() called multiple times'));
- }
-
- // finished
- function done(err) {
- if (self.timedOut) return;
- if (finished) return multiple();
- self.clearTimeout();
- self.duration = new Date - start;
- finished = true;
- fn(err);
- }
-
- // for .resetTimeout()
- this.callback = done;
-
- // async
- if (this.async) {
- try {
- this.fn.call(ctx, function(err){
- if (err instanceof Error) return done(err);
- if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
- done();
- });
- } catch (err) {
- done(err);
- }
- return;
- }
-
- // sync
- try {
- if (!this.pending) this.fn.call(ctx);
- this.duration = new Date - start;
- fn();
- } catch (err) {
- fn(err);
- }
-};
-
-}); // module: runnable.js
-
-require.register("runner.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var EventEmitter = require('browser/events').EventEmitter
- , debug = require('browser/debug')('runner')
- , Test = require('./test')
- , utils = require('./utils')
- , noop = function(){};
-
-/**
- * Expose `Runner`.
- */
-
-module.exports = Runner;
-
-/**
- * Initialize a `Runner` for the given `suite`.
- *
- * Events:
- *
- * - `start` execution started
- * - `end` execution complete
- * - `suite` (suite) test suite execution started
- * - `suite end` (suite) all tests (and sub-suites) have finished
- * - `test` (test) test execution started
- * - `test end` (test) test completed
- * - `hook` (hook) hook execution started
- * - `hook end` (hook) hook complete
- * - `pass` (test) test passed
- * - `fail` (test, err) test failed
- *
- * @api public
- */
-
-function Runner(suite) {
- var self = this;
- this._globals = [];
- this.suite = suite;
- this.total = suite.total();
- this.failures = 0;
- this.on('test end', function(test){ self.checkGlobals(test); });
- this.on('hook end', function(hook){ self.checkGlobals(hook); });
- this.grep(/.*/);
- this.globals(utils.keys(global).concat(['errno']));
-}
-
-/**
- * Inherit from `EventEmitter.prototype`.
- */
-
-Runner.prototype = new EventEmitter;
-Runner.prototype.constructor = Runner;
-
-
-/**
- * Run tests with full titles matching `re`. Updates runner.total
- * with number of tests matched.
- *
- * @param {RegExp} re
- * @return {Runner} for chaining
- * @api public
- */
-
-Runner.prototype.grep = function(re){
- debug('grep %s', re);
- this._grep = re;
- this.total = this.grepTotal(this.suite);
- return this;
-};
-
-/**
- * Returns the number of tests matching the grep search for the
- * given suite.
- *
- * @param {Suite} suite
- * @return {Number}
- * @api public
- */
-
-Runner.prototype.grepTotal = function(suite) {
- var self = this;
- var total = 0;
-
- suite.eachTest(function(test){
- if (self._grep.test(test.fullTitle())) total++;
- });
-
- return total;
-};
-
-/**
- * Allow the given `arr` of globals.
- *
- * @param {Array} arr
- * @return {Runner} for chaining
- * @api public
- */
-
-Runner.prototype.globals = function(arr){
- if (0 == arguments.length) return this._globals;
- debug('globals %j', arr);
- utils.forEach(arr, function(arr){
- this._globals.push(arr);
- }, this);
- return this;
-};
-
-/**
- * Check for global variable leaks.
- *
- * @api private
- */
-
-Runner.prototype.checkGlobals = function(test){
- if (this.ignoreLeaks) return;
-
- var leaks = utils.filter(utils.keys(global), function(key){
- return !~utils.indexOf(this._globals, key) && (!global.navigator || 'onerror' !== key);
- }, this);
-
- this._globals = this._globals.concat(leaks);
-
- if (leaks.length > 1) {
- this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + ''));
- } else if (leaks.length) {
- this.fail(test, new Error('global leak detected: ' + leaks[0]));
- }
-};
-
-/**
- * Fail the given `test`.
- *
- * @param {Test} test
- * @param {Error} err
- * @api private
- */
-
-Runner.prototype.fail = function(test, err){
- ++this.failures;
- test.state = 'failed';
- if ('string' == typeof err) {
- err = new Error('the string "' + err + '" was thrown, throw an Error :)');
- }
- this.emit('fail', test, err);
-};
-
-/**
- * Fail the given `hook` with `err`.
- *
- * Hook failures (currently) hard-end due
- * to that fact that a failing hook will
- * surely cause subsequent tests to fail,
- * causing jumbled reporting.
- *
- * @param {Hook} hook
- * @param {Error} err
- * @api private
- */
-
-Runner.prototype.failHook = function(hook, err){
- this.fail(hook, err);
- this.emit('end');
-};
-
-/**
- * Run hook `name` callbacks and then invoke `fn()`.
- *
- * @param {String} name
- * @param {Function} function
- * @api private
- */
-
-Runner.prototype.hook = function(name, fn){
- var suite = this.suite
- , hooks = suite['_' + name]
- , ms = suite._timeout
- , self = this
- , timer;
-
- function next(i) {
- var hook = hooks[i];
- if (!hook) return fn();
- self.currentRunnable = hook;
-
- self.emit('hook', hook);
-
- hook.on('error', function(err){
- self.failHook(hook, err);
- });
-
- hook.run(function(err){
- hook.removeAllListeners('error');
- if (err) return self.failHook(hook, err);
- self.emit('hook end', hook);
- next(++i);
- });
- }
-
- process.nextTick(function(){
- next(0);
- });
-};
-
-/**
- * Run hook `name` for the given array of `suites`
- * in order, and callback `fn(err)`.
- *
- * @param {String} name
- * @param {Array} suites
- * @param {Function} fn
- * @api private
- */
-
-Runner.prototype.hooks = function(name, suites, fn){
- var self = this
- , orig = this.suite;
-
- function next(suite) {
- self.suite = suite;
-
- if (!suite) {
- self.suite = orig;
- return fn();
- }
-
- self.hook(name, function(err){
- if (err) {
- self.suite = orig;
- return fn(err);
- }
-
- next(suites.pop());
- });
- }
-
- next(suites.pop());
-};
-
-/**
- * Run hooks from the top level down.
- *
- * @param {String} name
- * @param {Function} fn
- * @api private
- */
-
-Runner.prototype.hookUp = function(name, fn){
- var suites = [this.suite].concat(this.parents()).reverse();
- this.hooks(name, suites, fn);
-};
-
-/**
- * Run hooks from the bottom up.
- *
- * @param {String} name
- * @param {Function} fn
- * @api private
- */
-
-Runner.prototype.hookDown = function(name, fn){
- var suites = [this.suite].concat(this.parents());
- this.hooks(name, suites, fn);
-};
-
-/**
- * Return an array of parent Suites from
- * closest to furthest.
- *
- * @return {Array}
- * @api private
- */
-
-Runner.prototype.parents = function(){
- var suite = this.suite
- , suites = [];
- while (suite = suite.parent) suites.push(suite);
- return suites;
-};
-
-/**
- * Run the current test and callback `fn(err)`.
- *
- * @param {Function} fn
- * @api private
- */
-
-Runner.prototype.runTest = function(fn){
- var test = this.test
- , self = this;
-
- try {
- test.on('error', function(err){
- self.fail(test, err);
- });
- test.run(fn);
- } catch (err) {
- fn(err);
- }
-};
-
-/**
- * Run tests in the given `suite` and invoke
- * the callback `fn()` when complete.
- *
- * @param {Suite} suite
- * @param {Function} fn
- * @api private
- */
-
-Runner.prototype.runTests = function(suite, fn){
- var self = this
- , tests = suite.tests
- , test;
-
- function next(err) {
- // if we bail after first err
- if (self.failures && suite._bail) return fn();
-
- // next test
- test = tests.shift();
-
- // all done
- if (!test) return fn();
-
- // grep
- if (!self._grep.test(test.fullTitle())) return next();
-
- // pending
- if (test.pending) {
- self.emit('pending', test);
- self.emit('test end', test);
- return next();
- }
-
- // execute test and hook(s)
- self.emit('test', self.test = test);
- self.hookDown('beforeEach', function(){
- self.currentRunnable = self.test;
- self.runTest(function(err){
- test = self.test;
-
- if (err) {
- self.fail(test, err);
- self.emit('test end', test);
- return self.hookUp('afterEach', next);
- }
-
- test.state = 'passed';
- self.emit('pass', test);
- self.emit('test end', test);
- self.hookUp('afterEach', next);
- });
- });
- }
-
- this.next = next;
- next();
-};
-
-/**
- * Run the given `suite` and invoke the
- * callback `fn()` when complete.
- *
- * @param {Suite} suite
- * @param {Function} fn
- * @api private
- */
-
-Runner.prototype.runSuite = function(suite, fn){
- var total = this.grepTotal(suite)
- , self = this
- , i = 0;
-
- debug('run suite %s', suite.fullTitle());
-
- if (!total) return fn();
-
- this.emit('suite', this.suite = suite);
-
- function next() {
- var curr = suite.suites[i++];
- if (!curr) return done();
- self.runSuite(curr, next);
- }
-
- function done() {
- self.suite = suite;
- self.hook('afterAll', function(){
- self.emit('suite end', suite);
- fn();
- });
- }
-
- this.hook('beforeAll', function(){
- self.runTests(suite, next);
- });
-};
-
-/**
- * Handle uncaught exceptions.
- *
- * @param {Error} err
- * @api private
- */
-
-Runner.prototype.uncaught = function(err){
- debug('uncaught exception');
- var runnable = this.currentRunnable;
- if ('failed' == runnable.state) return;
- runnable.clearTimeout();
- err.uncaught = true;
- this.fail(runnable, err);
-
- // recover from test
- if ('test' == runnable.type) {
- this.emit('test end', runnable);
- this.hookUp('afterEach', this.next);
- return;
- }
-
- // bail on hooks
- this.emit('end');
-};
-
-/**
- * Run the root suite and invoke `fn(failures)`
- * on completion.
- *
- * @param {Function} fn
- * @return {Runner} for chaining
- * @api public
- */
-
-Runner.prototype.run = function(fn){
- var self = this
- , fn = fn || function(){};
-
- debug('start');
-
- // callback
- this.on('end', function(){
- debug('end');
- process.removeListener('uncaughtException', this.uncaught);
- fn(self.failures);
- });
-
- // run suites
- this.emit('start');
- this.runSuite(this.suite, function(){
- debug('finished running');
- self.emit('end');
- });
-
- // uncaught exception
- process.on('uncaughtException', function(err){
- self.uncaught(err);
- });
-
- return this;
-};
-
-}); // module: runner.js
-
-require.register("suite.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var EventEmitter = require('browser/events').EventEmitter
- , debug = require('browser/debug')('suite')
- , utils = require('./utils')
- , Hook = require('./hook');
-
-/**
- * Expose `Suite`.
- */
-
-exports = module.exports = Suite;
-
-/**
- * Create a new `Suite` with the given `title`
- * and parent `Suite`. When a suite with the
- * same title is already present, that suite
- * is returned to provide nicer reporter
- * and more flexible meta-testing.
- *
- * @param {Suite} parent
- * @param {String} title
- * @return {Suite}
- * @api public
- */
-
-exports.create = function(parent, title){
- var suite = new Suite(title, parent.ctx);
- suite.parent = parent;
- title = suite.fullTitle();
- parent.addSuite(suite);
- return suite;
-};
-
-/**
- * Initialize a new `Suite` with the given
- * `title` and `ctx`.
- *
- * @param {String} title
- * @param {Context} ctx
- * @api private
- */
-
-function Suite(title, ctx) {
- this.title = title;
- this.ctx = ctx;
- this.suites = [];
- this.tests = [];
- this._beforeEach = [];
- this._beforeAll = [];
- this._afterEach = [];
- this._afterAll = [];
- this.root = !title;
- this._timeout = 2000;
- this._bail = false;
-}
-
-/**
- * Inherit from `EventEmitter.prototype`.
- */
-
-Suite.prototype = new EventEmitter;
-Suite.prototype.constructor = Suite;
-
-
-/**
- * Return a clone of this `Suite`.
- *
- * @return {Suite}
- * @api private
- */
-
-Suite.prototype.clone = function(){
- var suite = new Suite(this.title);
- debug('clone');
- suite.ctx = this.ctx;
- suite.timeout(this.timeout());
- suite.bail(this.bail());
- return suite;
-};
-
-/**
- * Set timeout `ms` or short-hand such as "2s".
- *
- * @param {Number|String} ms
- * @return {Suite|Number} for chaining
- * @api private
- */
-
-Suite.prototype.timeout = function(ms){
- if (0 == arguments.length) return this._timeout;
- if (String(ms).match(/s$/)) ms = parseFloat(ms) * 1000;
- debug('timeout %d', ms);
- this._timeout = parseInt(ms, 10);
- return this;
-};
-
-/**
- * Sets whether to bail after first error.
- *
- * @parma {Boolean} bail
- * @return {Suite|Number} for chaining
- * @api private
- */
-
-Suite.prototype.bail = function(bail){
- if (0 == arguments.length) return this._bail;
- debug('bail %s', bail);
- this._bail = bail;
- return this;
-};
-
-/**
- * Run `fn(test[, done])` before running tests.
- *
- * @param {Function} fn
- * @return {Suite} for chaining
- * @api private
- */
-
-Suite.prototype.beforeAll = function(fn){
- var hook = new Hook('"before all" hook', fn);
- hook.parent = this;
- hook.timeout(this.timeout());
- hook.ctx = this.ctx;
- this._beforeAll.push(hook);
- this.emit('beforeAll', hook);
- return this;
-};
-
-/**
- * Run `fn(test[, done])` after running tests.
- *
- * @param {Function} fn
- * @return {Suite} for chaining
- * @api private
- */
-
-Suite.prototype.afterAll = function(fn){
- var hook = new Hook('"after all" hook', fn);
- hook.parent = this;
- hook.timeout(this.timeout());
- hook.ctx = this.ctx;
- this._afterAll.push(hook);
- this.emit('afterAll', hook);
- return this;
-};
-
-/**
- * Run `fn(test[, done])` before each test case.
- *
- * @param {Function} fn
- * @return {Suite} for chaining
- * @api private
- */
-
-Suite.prototype.beforeEach = function(fn){
- var hook = new Hook('"before each" hook', fn);
- hook.parent = this;
- hook.timeout(this.timeout());
- hook.ctx = this.ctx;
- this._beforeEach.push(hook);
- this.emit('beforeEach', hook);
- return this;
-};
-
-/**
- * Run `fn(test[, done])` after each test case.
- *
- * @param {Function} fn
- * @return {Suite} for chaining
- * @api private
- */
-
-Suite.prototype.afterEach = function(fn){
- var hook = new Hook('"after each" hook', fn);
- hook.parent = this;
- hook.timeout(this.timeout());
- hook.ctx = this.ctx;
- this._afterEach.push(hook);
- this.emit('afterEach', hook);
- return this;
-};
-
-/**
- * Add a test `suite`.
- *
- * @param {Suite} suite
- * @return {Suite} for chaining
- * @api private
- */
-
-Suite.prototype.addSuite = function(suite){
- suite.parent = this;
- suite.timeout(this.timeout());
- suite.bail(this.bail());
- this.suites.push(suite);
- this.emit('suite', suite);
- return this;
-};
-
-/**
- * Add a `test` to this suite.
- *
- * @param {Test} test
- * @return {Suite} for chaining
- * @api private
- */
-
-Suite.prototype.addTest = function(test){
- test.parent = this;
- test.timeout(this.timeout());
- test.ctx = this.ctx;
- this.tests.push(test);
- this.emit('test', test);
- return this;
-};
-
-/**
- * Return the full title generated by recursively
- * concatenating the parent's full title.
- *
- * @return {String}
- * @api public
- */
-
-Suite.prototype.fullTitle = function(){
- if (this.parent) {
- var full = this.parent.fullTitle();
- if (full) return full + ' ' + this.title;
- }
- return this.title;
-};
-
-/**
- * Return the total number of tests.
- *
- * @return {Number}
- * @api public
- */
-
-Suite.prototype.total = function(){
- return utils.reduce(this.suites, function(sum, suite){
- return sum + suite.total();
- }, 0) + this.tests.length;
-};
-
-/**
- * Iterates through each suite recursively to find
- * all tests. Applies a function in the format
- * `fn(test)`.
- *
- * @param {Function} fn
- * @return {Suite}
- * @api private
- */
-
-Suite.prototype.eachTest = function(fn){
- utils.forEach(this.tests, fn);
- utils.forEach(this.suites, function(suite){
- suite.eachTest(fn);
- });
- return this;
-};
-
-}); // module: suite.js
-
-require.register("test.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var Runnable = require('./runnable');
-
-/**
- * Expose `Test`.
- */
-
-module.exports = Test;
-
-/**
- * Initialize a new `Test` with the given `title` and callback `fn`.
- *
- * @param {String} title
- * @param {Function} fn
- * @api private
- */
-
-function Test(title, fn) {
- Runnable.call(this, title, fn);
- this.pending = !fn;
- this.type = 'test';
-}
-
-/**
- * Inherit from `Runnable.prototype`.
- */
-
-Test.prototype = new Runnable;
-Test.prototype.constructor = Test;
-
-
-/**
- * Inspect the context void of private properties.
- *
- * @return {String}
- * @api private
- */
-
-Test.prototype.inspect = function(){
- return JSON.stringify(this, function(key, val){
- return '_' == key[0]
- ? undefined
- : 'parent' == key
- ? '#<Suite>'
- : val;
- }, 2);
-};
-}); // module: test.js
-
-require.register("utils.js", function(module, exports, require){
-
-/**
- * Module dependencies.
- */
-
-var fs = require('browser/fs')
- , path = require('browser/path')
- , join = path.join
- , debug = require('browser/debug')('watch');
-
-/**
- * Ignored directories.
- */
-
-var ignore = ['node_modules', '.git'];
-
-/**
- * Escape special characters in the given string of html.
- *
- * @param {String} html
- * @return {String}
- * @api private
- */
-
-exports.escape = function(html) {
- return String(html)
- .replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/</g, '<')
- .replace(/>/g, '>');
-};
-
-/**
- * Array#forEach (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @param {Object} scope
- * @api private
- */
-
-exports.forEach = function(arr, fn, scope) {
- for (var i = 0, l = arr.length; i < l; i++)
- fn.call(scope, arr[i], i);
-};
-
-/**
- * Array#indexOf (<=IE8)
- *
- * @parma {Array} arr
- * @param {Object} obj to find index of
- * @param {Number} start
- * @api private
- */
-
-exports.indexOf = function (arr, obj, start) {
- for (var i = start || 0, l = arr.length; i < l; i++) {
- if (arr[i] === obj)
- return i;
- }
- return -1;
-};
-
-/**
- * Array#reduce (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @param {Object} initial value
- * @param {Object} scope
- * @api private
- */
-
-exports.reduce = function(arr, fn, val, scope) {
- var rval = val;
-
- for (var i = 0, l = arr.length; i < l; i++) {
- rval = fn.call(scope, rval, arr[i], i, arr);
- }
-
- return rval;
-};
-
-/**
- * Array#filter (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @param {Object} scope
- * @api private
- */
-
-exports.filter = function(arr, fn, scope) {
- var ret = [];
-
- for (var i = 0, l = arr.length; i < l; i++) {
- var val = arr[i];
- if (fn.call(scope, val, i, arr))
- ret.push(val);
- }
-
- return ret;
-};
-
-/**
- * Object.keys (<=IE8)
- *
- * @param {Object} obj
- * @return {Array} keys
- * @api private
- */
-
-exports.keys = Object.keys || function(obj) {
- var keys = []
- , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
-
- for (var key in obj) {
- if (has.call(obj, key)) {
- keys.push(key);
- }
- }
-
- return keys;
-};
-
-/**
- * Watch the given `files` for changes
- * and invoke `fn(file)` on modification.
- *
- * @param {Array} files
- * @param {Function} fn
- * @api private
- */
-
-exports.watch = function(files, fn){
- var options = { interval: 100 };
- files.forEach(function(file){
- debug('file %s', file);
- fs.watchFile(file, options, function(curr, prev){
- if (prev.mtime < curr.mtime) fn(file);
- });
- });
-};
-
-/**
- * Ignored files.
- */
-
-function ignored(path){
- return !~ignore.indexOf(path);
-}
-
-/**
- * Lookup files in the given `dir`.
- *
- * @return {Array}
- * @api private
- */
-
-exports.files = function(dir, ret){
- ret = ret || [];
-
- fs.readdirSync(dir)
- .filter(ignored)
- .forEach(function(path){
- path = join(dir, path);
- if (fs.statSync(path).isDirectory()) {
- exports.files(path, ret);
- } else if (path.match(/\.(js|coffee)$/)) {
- ret.push(path);
- }
- });
-
- return ret;
-};
-
-/**
- * Compute a slug from the given `str`.
- *
- * @param {String} str
- * @return {String}
- */
-
-exports.slug = function(str){
- return str
- .toLowerCase()
- .replace(/ +/g, '-')
- .replace(/[^-\w]/g, '');
-};
-}); // module: utils.js
-/**
- * Node shims.
- *
- * These are meant only to allow
- * mocha.js to run untouched, not
- * to allow running node code in
- * the browser.
- */
-
-process = {};
-process.exit = function(status){};
-process.stdout = {};
-global = window;
-
-/**
- * next tick implementation.
- */
-
-process.nextTick = (function(){
- // postMessage behaves badly on IE8
- if (window.ActiveXObject || !window.postMessage) {
- return function(fn){ fn() };
- }
-
- // based on setZeroTimeout by David Baron
- // - http://dbaron.org/log/20100309-faster-timeouts
- var timeouts = []
- , name = 'mocha-zero-timeout'
-
- window.addEventListener('message', function(e){
- if (e.source == window && e.data == name) {
- if (e.stopPropagation) e.stopPropagation();
- if (timeouts.length) timeouts.shift()();
- }
- }, true);
-
- return function(fn){
- timeouts.push(fn);
- window.postMessage(name, '*');
- }
-})();
-
-/**
- * Remove uncaughtException listener.
- */
-
-process.removeListener = function(e){
- if ('uncaughtException' == e) {
- window.onerror = null;
- }
-};
-
-/**
- * Implements uncaughtException listener.
- */
-
-process.on = function(e, fn){
- if ('uncaughtException' == e) {
- window.onerror = fn;
- }
-};
-
-/**
- * Expose mocha.
- */
-
-window.mocha = require('mocha');
-
-// boot
-;(function(){
- var suite = new mocha.Suite('', new mocha.Context)
- , utils = mocha.utils
- , options = {}
-
- /**
- * Highlight the given string of `js`.
- */
-
- function highlight(js) {
- return js
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
- .replace(/('.*?')/gm, '<span class="string">$1</span>')
- .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
- .replace(/(\d+)/gm, '<span class="number">$1</span>')
- .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
- .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
- }
-
- /**
- * Highlight code contents.
- */
-
- function highlightCode() {
- var code = document.getElementsByTagName('code');
- for (var i = 0, len = code.length; i < len; ++i) {
- code[i].innerHTML = highlight(code[i].innerHTML);
- }
- }
-
- /**
- * Parse the given `qs`.
- */
-
- function parse(qs) {
- return utils.reduce(qs.replace('?', '').split('&'), function(obj, pair){
- var i = pair.indexOf('=')
- , key = pair.slice(0, i)
- , val = pair.slice(++i);
-
- obj[key] = decodeURIComponent(val);
- return obj;
- }, {});
- }
-
- /**
- * Setup mocha with the given setting options.
- */
-
- mocha.setup = function(opts){
- if ('string' === typeof opts) options.ui = opts;
- else options = opts;
-
- ui = mocha.interfaces[options.ui];
- if (!ui) throw new Error('invalid mocha interface "' + ui + '"');
- if (options.timeout) suite.timeout(options.timeout);
- ui(suite);
- suite.emit('pre-require', window);
- };
-
- /**
- * Run mocha, returning the Runner.
- */
-
- mocha.run = function(fn){
- suite.emit('run');
- var runner = new mocha.Runner(suite);
- var Reporter = options.reporter || mocha.reporters.HTML;
- var reporter = new Reporter(runner);
- var query = parse(window.location.search || "");
- if (query.grep) runner.grep(new RegExp(query.grep));
- if (options.ignoreLeaks) runner.ignoreLeaks = true;
- if (options.globals) runner.globals(options.globals);
- runner.globals(['location']);
- runner.on('end', highlightCode);
- return runner.run(fn);
- };
-})();
-})();
\ No newline at end of file
+++ /dev/null
-
-/**
- * Require the given path.
- *
- * @param {String} path
- * @return {Object} exports
- * @api public
- */
-
-function require(p, parent){
- var path = require.resolve(p)
- , mod = require.modules[path];
- if (!mod) throw new Error('failed to require "' + p + '" in ' + parent);
- if (!mod.exports) {
- mod.exports = {};
- mod.client = true;
- mod.call(mod.exports, mod, mod.exports, require.relative(path));
- }
- return mod.exports;
-}
-
-/**
- * Registered modules.
- */
-
-require.modules = {};
-
-/**
- * Resolve `path`.
- *
- * @param {String} path
- * @return {Object} module
- * @api public
- */
-
-require.resolve = function(path){
- var orig = path
- , reg = path + '.js'
- , index = path + '/index.js';
- return require.modules[reg] && reg
- || require.modules[index] && index
- || orig;
-};
-
-/**
- * Register module at `path` with callback `fn`.
- *
- * @param {String} path
- * @param {Function} fn
- * @api public
- */
-
-require.register = function(path, fn){
- require.modules[path] = fn;
-};
-
-/**
- * Defines and executes anonymous module immediately, while preserving relative
- * paths.
- *
- * @param {String} path
- * @param {Function} require ref
- * @api public
- */
-
-require.exec = function (path, fn) {
- fn.call(window, require.relative(path));
-};
-
-/**
- * Return a require function relative to the `parent` path.
- *
- * @param {String} parent
- * @return {Function}
- * @api private
- */
-
-require.relative = function(parent) {
- return function(p){
- if ('.' != p[0]) return require(p);
-
- var path = parent.split('/')
- , segs = p.split('/');
- path.pop();
-
- for (var i = 0; i < segs.length; i++) {
- var seg = segs[i];
- if ('..' == seg) path.pop();
- else if ('.' != seg) path.push(seg);
- }
-
- return require(path.join('/'), parent);
- };
-};
-// component qs: querystring
-require.register("querystring", function(module, exports, require){
-;(function(){
-
-/**
- * Object#toString() ref for stringify().
- */
-
-var toString = Object.prototype.toString;
-
-/**
- * Cache non-integer test regexp.
- */
-
-var isint = /^[0-9]+$/;
-
-function promote(parent, key) {
- if (parent[key].length == 0) return parent[key] = {};
- var t = {};
- for (var i in parent[key]) t[i] = parent[key][i];
- parent[key] = t;
- return t;
-}
-
-function parse(parts, parent, key, val) {
- var part = parts.shift();
- // end
- if (!part) {
- if (Array.isArray(parent[key])) {
- parent[key].push(val);
- } else if ('object' == typeof parent[key]) {
- parent[key] = val;
- } else if ('undefined' == typeof parent[key]) {
- parent[key] = val;
- } else {
- parent[key] = [parent[key], val];
- }
- // array
- } else {
- var obj = parent[key] = parent[key] || [];
- if (']' == part) {
- if (Array.isArray(obj)) {
- if ('' != val) obj.push(val);
- } else if ('object' == typeof obj) {
- obj[Object.keys(obj).length] = val;
- } else {
- obj = parent[key] = [parent[key], val];
- }
- // prop
- } else if (~part.indexOf(']')) {
- part = part.substr(0, part.length - 1);
- if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
- parse(parts, obj, part, val);
- // key
- } else {
- if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
- parse(parts, obj, part, val);
- }
- }
-}
-
-/**
- * Merge parent key/val pair.
- */
-
-function merge(parent, key, val){
- if (~key.indexOf(']')) {
- var parts = key.split('[')
- , len = parts.length
- , last = len - 1;
- parse(parts, parent, 'base', val);
- // optimize
- } else {
- if (!isint.test(key) && Array.isArray(parent.base)) {
- var t = {};
- for (var k in parent.base) t[k] = parent.base[k];
- parent.base = t;
- }
- set(parent.base, key, val);
- }
-
- return parent;
-}
-
-/**
- * Parse the given obj.
- */
-
-function parseObject(obj){
- var ret = { base: {} };
- Object.keys(obj).forEach(function(name){
- merge(ret, name, obj[name]);
- });
- return ret.base;
-}
-
-/**
- * Parse the given str.
- */
-
-function parseString(str){
- return String(str)
- .split('&')
- .reduce(function(ret, pair){
- try{
- pair = decodeURIComponent(pair.replace(/\+/g, ' '));
- } catch(e) {
- // ignore
- }
-
- var eql = pair.indexOf('=')
- , brace = lastBraceInKey(pair)
- , key = pair.substr(0, brace || eql)
- , val = pair.substr(brace || eql, pair.length)
- , val = val.substr(val.indexOf('=') + 1, val.length);
-
- // ?foo
- if ('' == key) key = pair, val = '';
-
- return merge(ret, key, val);
- }, { base: {} }).base;
-}
-
-/**
- * Parse the given query `str` or `obj`, returning an object.
- *
- * @param {String} str | {Object} obj
- * @return {Object}
- * @api public
- */
-
-exports.parse = function(str){
- if (null == str || '' == str) return {};
- return 'object' == typeof str
- ? parseObject(str)
- : parseString(str);
-};
-
-/**
- * Turn the given `obj` into a query string
- *
- * @param {Object} obj
- * @return {String}
- * @api public
- */
-
-var stringify = exports.stringify = function(obj, prefix) {
- if (Array.isArray(obj)) {
- return stringifyArray(obj, prefix);
- } else if ('[object Object]' == toString.call(obj)) {
- return stringifyObject(obj, prefix);
- } else if ('string' == typeof obj) {
- return stringifyString(obj, prefix);
- } else {
- return prefix + '=' + obj;
- }
-};
-
-/**
- * Stringify the given `str`.
- *
- * @param {String} str
- * @param {String} prefix
- * @return {String}
- * @api private
- */
-
-function stringifyString(str, prefix) {
- if (!prefix) throw new TypeError('stringify expects an object');
- return prefix + '=' + encodeURIComponent(str);
-}
-
-/**
- * Stringify the given `arr`.
- *
- * @param {Array} arr
- * @param {String} prefix
- * @return {String}
- * @api private
- */
-
-function stringifyArray(arr, prefix) {
- var ret = [];
- if (!prefix) throw new TypeError('stringify expects an object');
- for (var i = 0; i < arr.length; i++) {
- ret.push(stringify(arr[i], prefix + '['+i+']'));
- }
- return ret.join('&');
-}
-
-/**
- * Stringify the given `obj`.
- *
- * @param {Object} obj
- * @param {String} prefix
- * @return {String}
- * @api private
- */
-
-function stringifyObject(obj, prefix) {
- var ret = []
- , keys = Object.keys(obj)
- , key;
-
- for (var i = 0, len = keys.length; i < len; ++i) {
- key = keys[i];
- ret.push(stringify(obj[key], prefix
- ? prefix + '[' + encodeURIComponent(key) + ']'
- : encodeURIComponent(key)));
- }
-
- return ret.join('&');
-}
-
-/**
- * Set `obj`'s `key` to `val` respecting
- * the weird and wonderful syntax of a qs,
- * where "foo=bar&foo=baz" becomes an array.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {String} val
- * @api private
- */
-
-function set(obj, key, val) {
- var v = obj[key];
- if (undefined === v) {
- obj[key] = val;
- } else if (Array.isArray(v)) {
- v.push(val);
- } else {
- obj[key] = [v, val];
- }
-}
-
-/**
- * Locate last brace in `str` within the key.
- *
- * @param {String} str
- * @return {Number}
- * @api private
- */
-
-function lastBraceInKey(str) {
- var len = str.length
- , brace
- , c;
- for (var i = 0; i < len; ++i) {
- c = str[i];
- if (']' == c) brace = false;
- if ('[' == c) brace = true;
- if ('=' == c && !brace) return i;
- }
-}
-})();
-});
+++ /dev/null
-
-if (require.register) {
- var qs = require('querystring');
-} else {
- var qs = require('../')
- , expect = require('expect.js');
-}
-
-describe('qs.parse()', function(){
- it('should support the basics', function(){
- expect(qs.parse('0=foo')).to.eql({ '0': 'foo' });
-
- expect(qs.parse('foo=c++'))
- .to.eql({ foo: 'c ' });
-
- expect(qs.parse('a[>=]=23'))
- .to.eql({ a: { '>=': '23' }});
-
- expect(qs.parse('a[<=>]==23'))
- .to.eql({ a: { '<=>': '=23' }});
-
- expect(qs.parse('a[==]=23'))
- .to.eql({ a: { '==': '23' }});
-
- expect(qs.parse('foo'))
- .to.eql({ foo: '' });
-
- expect(qs.parse('foo=bar'))
- .to.eql({ foo: 'bar' });
-
- expect(qs.parse(' foo = bar = baz '))
- .to.eql({ ' foo ': ' bar = baz ' });
-
- expect(qs.parse('foo=bar=baz'))
- .to.eql({ foo: 'bar=baz' });
-
- expect(qs.parse('foo=bar&bar=baz'))
- .to.eql({ foo: 'bar', bar: 'baz' });
-
- expect(qs.parse('foo=bar&baz'))
- .to.eql({ foo: 'bar', baz: '' });
-
- expect(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'))
- .to.eql({
- cht: 'p3'
- , chd: 't:60,40'
- , chs: '250x100'
- , chl: 'Hello|World'
- });
- })
-
- it('should support encoded = signs', function(){
- expect(qs.parse('he%3Dllo=th%3Dere'))
- .to.eql({ 'he=llo': 'th=ere' });
- })
-
- it('should support nesting', function(){
- expect(qs.parse('ops[>=]=25'))
- .to.eql({ ops: { '>=': '25' }});
-
- expect(qs.parse('user[name]=tj'))
- .to.eql({ user: { name: 'tj' }});
-
- expect(qs.parse('user[name][first]=tj&user[name][last]=holowaychuk'))
- .to.eql({ user: { name: { first: 'tj', last: 'holowaychuk' }}});
- })
-
- it('should support array notation', function(){
- expect(qs.parse('images[]'))
- .to.eql({ images: [] });
-
- expect(qs.parse('user[]=tj'))
- .to.eql({ user: ['tj'] });
-
- expect(qs.parse('user[]=tj&user[]=tobi&user[]=jane'))
- .to.eql({ user: ['tj', 'tobi', 'jane'] });
-
- expect(qs.parse('user[names][]=tj&user[names][]=tyler'))
- .to.eql({ user: { names: ['tj', 'tyler'] }});
-
- expect(qs.parse('user[names][]=tj&user[names][]=tyler&user[email]=tj@vision-media.ca'))
- .to.eql({ user: { names: ['tj', 'tyler'], email: 'tj@vision-media.ca' }});
-
- expect(qs.parse('items=a&items=b'))
- .to.eql({ items: ['a', 'b'] });
-
- expect(qs.parse('user[names]=tj&user[names]=holowaychuk&user[names]=TJ'))
- .to.eql({ user: { names: ['tj', 'holowaychuk', 'TJ'] }});
-
- expect(qs.parse('user[name][first]=tj&user[name][first]=TJ'))
- .to.eql({ user: { name: { first: ['tj', 'TJ'] }}});
-
- var o = qs.parse('existing[fcbaebfecc][name][last]=tj')
- expect(o).to.eql({ existing: { 'fcbaebfecc': { name: { last: 'tj' }}}})
- expect(Array.isArray(o.existing)).to.equal(false);
- })
-
- it('should support arrays with indexes', function(){
- expect(qs.parse('foo[0]=bar&foo[1]=baz')).to.eql({ foo: ['bar', 'baz'] });
- expect(qs.parse('foo[1]=bar&foo[0]=baz')).to.eql({ foo: ['baz', 'bar'] });
- expect(qs.parse('foo[base64]=RAWR')).to.eql({ foo: { base64: 'RAWR' }});
- expect(qs.parse('foo[64base]=RAWR')).to.eql({ foo: { '64base': 'RAWR' }});
- })
-
- it('should expand to an array when dupliate keys are present', function(){
- expect(qs.parse('items=bar&items=baz&items=raz'))
- .to.eql({ items: ['bar', 'baz', 'raz'] });
- })
-
- it('should support right-hand side brackets', function(){
- expect(qs.parse('pets=["tobi"]'))
- .to.eql({ pets: '["tobi"]' });
-
- expect(qs.parse('operators=[">=", "<="]'))
- .to.eql({ operators: '[">=", "<="]' });
-
- expect(qs.parse('op[>=]=[1,2,3]'))
- .to.eql({ op: { '>=': '[1,2,3]' }});
-
- expect(qs.parse('op[>=]=[1,2,3]&op[=]=[[[[1]]]]'))
- .to.eql({ op: { '>=': '[1,2,3]', '=': '[[[[1]]]]' }});
- })
-
- it('should support empty values', function(){
- expect(qs.parse('')).to.eql({});
- expect(qs.parse(undefined)).to.eql({});
- expect(qs.parse(null)).to.eql({});
- })
-
- it('should transform arrays to objects', function(){
- expect(qs.parse('foo[0]=bar&foo[bad]=baz')).to.eql({ foo: { 0: "bar", bad: "baz" }});
- expect(qs.parse('foo[bad]=baz&foo[0]=bar')).to.eql({ foo: { 0: "bar", bad: "baz" }});
- })
-
- it('should support malformed uri chars', function(){
- expect(qs.parse('{%:%}')).to.eql({ '{%:%}': '' });
- expect(qs.parse('foo=%:%}')).to.eql({ 'foo': '%:%}' });
- })
-
- it('should support semi-parsed strings', function(){
- expect(qs.parse({ 'user[name]': 'tobi' }))
- .to.eql({ user: { name: 'tobi' }});
-
- expect(qs.parse({ 'user[name]': 'tobi', 'user[email][main]': 'tobi@lb.com' }))
- .to.eql({ user: { name: 'tobi', email: { main: 'tobi@lb.com' } }});
- })
-})
\ No newline at end of file
+++ /dev/null
-
-if (require.register) {
- var qs = require('querystring');
-} else {
- var qs = require('../')
- , expect = require('expect.js');
-}
-
-var date = new Date(0);
-
-var str_identities = {
- 'basics': [
- { str: 'foo=bar', obj: {'foo' : 'bar'}},
- { str: 'foo=%22bar%22', obj: {'foo' : '\"bar\"'}},
- { str: 'foo=', obj: {'foo': ''}},
- { str: 'foo=1&bar=2', obj: {'foo' : '1', 'bar' : '2'}},
- { str: 'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F', obj: {'my weird field': "q1!2\"'w$5&7/z8)?"}},
- { str: 'foo%3Dbaz=bar', obj: {'foo=baz': 'bar'}},
- { str: 'foo=bar&bar=baz', obj: {foo: 'bar', bar: 'baz'}},
- { str: 'foo=bar&baz=&raz=', obj: { foo: 'bar', baz: null, raz: undefined }}
- ],
- 'escaping': [
- { str: 'foo=foo%20bar', obj: {foo: 'foo bar'}},
- { str: 'cht=p3&chd=t%3A60%2C40&chs=250x100&chl=Hello%7CWorld', obj: {
- cht: 'p3'
- , chd: 't:60,40'
- , chs: '250x100'
- , chl: 'Hello|World'
- }}
- ],
- 'nested': [
- { str: 'foo[0]=bar&foo[1]=quux', obj: {'foo' : ['bar', 'quux']}},
- { str: 'foo[0]=bar', obj: {foo: ['bar']}},
- { str: 'foo[0]=1&foo[1]=2', obj: {'foo' : ['1', '2']}},
- { str: 'foo=bar&baz[0]=1&baz[1]=2&baz[2]=3', obj: {'foo' : 'bar', 'baz' : ['1', '2', '3']}},
- { str: 'foo[0]=bar&baz[0]=1&baz[1]=2&baz[2]=3', obj: {'foo' : ['bar'], 'baz' : ['1', '2', '3']}},
- { str: 'x[y][z]=1', obj: {'x' : {'y' : {'z' : '1'}}}},
- { str: 'x[y][z][0]=1', obj: {'x' : {'y' : {'z' : ['1']}}}},
- { str: 'x[y][z]=2', obj: {'x' : {'y' : {'z' : '2'}}}},
- { str: 'x[y][z][0]=1&x[y][z][1]=2', obj: {'x' : {'y' : {'z' : ['1', '2']}}}},
- { str: 'x[y][0][z]=1', obj: {'x' : {'y' : [{'z' : '1'}]}}},
- { str: 'x[y][0][z][0]=1', obj: {'x' : {'y' : [{'z' : ['1']}]}}},
- { str: 'x[y][0][z]=1&x[y][0][w]=2', obj: {'x' : {'y' : [{'z' : '1', 'w' : '2'}]}}},
- { str: 'x[y][0][v][w]=1', obj: {'x' : {'y' : [{'v' : {'w' : '1'}}]}}},
- { str: 'x[y][0][z]=1&x[y][0][v][w]=2', obj: {'x' : {'y' : [{'z' : '1', 'v' : {'w' : '2'}}]}}},
- { str: 'x[y][0][z]=1&x[y][1][z]=2', obj: {'x' : {'y' : [{'z' : '1'}, {'z' : '2'}]}}},
- { str: 'x[y][0][z]=1&x[y][0][w]=a&x[y][1][z]=2&x[y][1][w]=3', obj: {'x' : {'y' : [{'z' : '1', 'w' : 'a'}, {'z' : '2', 'w' : '3'}]}}},
- { str: 'user[name][first]=tj&user[name][last]=holowaychuk', obj: { user: { name: { first: 'tj', last: 'holowaychuk' }}}}
- ],
- 'errors': [
- { obj: 'foo=bar', message: 'stringify expects an object' },
- { obj: ['foo', 'bar'], message: 'stringify expects an object' }
- ],
- 'numbers': [
- { str: 'limit[0]=1&limit[1]=2&limit[2]=3', obj: { limit: [1, 2, '3'] }},
- { str: 'limit=1', obj: { limit: 1 }}
- ],
- 'others': [
- { str: 'at=' + encodeURIComponent(date), obj: { at: date } }
- ]
-};
-
-function test(type) {
- return function(){
- var str, obj;
- for (var i = 0; i < str_identities[type].length; i++) {
- str = str_identities[type][i].str;
- obj = str_identities[type][i].obj;
- expect(qs.stringify(obj)).to.eql(str);
- }
- }
-}
-
-describe('qs.stringify()', function(){
- it('should support the basics', test('basics'))
- it('should support escapes', test('escaping'))
- it('should support nesting', test('nested'))
- it('should support numbers', test('numbers'))
- it('should support others', test('others'))
-})
--- /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.
},
"name": "tunnel-agent",
"description": "HTTP proxy tunneling agent. Formerly part of mikeal/request, now a standalone module.",
- "version": "0.2.0",
+ "version": "0.3.0",
"repository": {
"url": "https://github.com/mikeal/tunnel-agent"
},
"engines": {
"node": "*"
},
- "_id": "tunnel-agent@0.2.0",
- "readme": "ERROR: No README.md file found!",
- "dist": {
- "shasum": "4927e7d29250f1d064e82922a5e608409cdb3cf4"
+ "readme": "tunnel-agent\n============\n\nHTTP proxy tunneling agent. Formerly part of mikeal/request, now a standalone module.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/mikeal/tunnel-agent/issues"
},
- "_from": "tunnel-agent@~0.2.0",
- "_resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.2.0.tgz"
+ "_id": "tunnel-agent@0.3.0",
+ "_from": "tunnel-agent@~0.3.0"
}
"util",
"utility"
],
- "version": "2.16.6",
+ "version": "2.21.0",
"author": {
"name": "Mikeal Rogers",
"email": "mikeal.rogers@gmail.com"
],
"main": "index.js",
"dependencies": {
- "form-data": "~0.0.3",
- "mime": "~1.2.7",
- "hawk": "~0.10.2",
+ "qs": "~0.6.0",
+ "json-stringify-safe": "~4.0.0",
+ "forever-agent": "~0.5.0",
+ "tunnel-agent": "~0.3.0",
+ "http-signature": "~0.9.11",
+ "hawk": "~0.13.0",
+ "aws-sign": "~0.3.0",
+ "oauth-sign": "~0.3.0",
+ "cookie-jar": "~0.3.0",
"node-uuid": "~1.4.0",
- "cookie-jar": "~0.2.0",
- "aws-sign": "~0.2.0",
- "oauth-sign": "~0.2.0",
- "forever-agent": "~0.2.0",
- "tunnel-agent": "~0.2.0",
- "json-stringify-safe": "~3.0.0",
- "qs": "~0.5.4"
+ "mime": "~1.2.9",
+ "form-data": "0.0.8"
},
"scripts": {
"test": "node tests/run.js"
},
- "readme": "# Request -- Simplified HTTP request method\n\n## Install\n\n<pre>\n npm install request\n</pre>\n\nOr from source:\n\n<pre>\n git clone git://github.com/mikeal/request.git \n cd request\n npm link\n</pre>\n\n## Super simple to use\n\nRequest is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.\n\n```javascript\nvar request = require('request');\nrequest('http://www.google.com', function (error, response, body) {\n if (!error && response.statusCode == 200) {\n console.log(body) // Print the google web page.\n }\n})\n```\n\n## Streaming\n\nYou can stream any response to a file stream.\n\n```javascript\nrequest('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))\n```\n\nYou 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 one is not already provided in the headers.\n\n```javascript\nfs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))\n```\n\nRequest can also pipe to itself. When doing so the content-type and content-length will be preserved in the PUT headers.\n\n```javascript\nrequest.get('http://google.com/img.png').pipe(request.put('http://mysite.com/img.png'))\n```\n\nNow let's get fancy.\n\n```javascript\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n if (req.method === 'PUT') {\n req.pipe(request.put('http://mysite.com/doodle.png'))\n } else if (req.method === 'GET' || req.method === 'HEAD') {\n request.get('http://mysite.com/doodle.png').pipe(resp)\n } \n }\n})\n```\n\nYou can also pipe() from a http.ServerRequest instance and to a http.ServerResponse instance. The HTTP method and headers will be sent as well as the entity-body data. Which means that, if you don't really care about security, you can do:\n\n```javascript\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n var x = request('http://mysite.com/doodle.png')\n req.pipe(x)\n x.pipe(resp)\n }\n})\n```\n\nAnd since pipe() returns the destination stream in node 0.5.x you can do one line proxying :)\n\n```javascript\nreq.pipe(request('http://mysite.com/doodle.png')).pipe(resp)\n```\n\nAlso, none of this new functionality conflicts with requests previous features, it just expands them.\n\n```javascript\nvar r = request.defaults({'proxy':'http://localproxy.com'})\n\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n r.get('http://google.com/doodle.png').pipe(resp)\n }\n})\n```\nYou can still use intermediate proxies, the requests will still follow HTTP forwards, etc.\n\n## Forms\n\n`request` supports `application/x-www-form-urlencoded` and `multipart/form-data` form uploads. For `multipart/related` refer to the `multipart` API.\n\nUrl encoded forms are simple\n\n```javascript\nrequest.post('http://service.com/upload', {form:{key:'value'}})\n// or\nrequest.post('http://service.com/upload').form({key:'value'})\n```\n\nFor `multipart/form-data` we use the [form-data](https://github.com/felixge/node-form-data) library by [@felixge](https://github.com/felixge). You don't need to worry about piping the form object or setting the headers, `request` will handle that for you.\n\n```javascript\nvar r = request.post('http://service.com/upload')\nvar form = r.form()\nform.append('my_field', 'my_value')\nform.append('my_buffer', new Buffer([1, 2, 3]))\nform.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png'))\nform.append('remote_file', request('http://google.com/doodle.png'))\n```\n\n## HTTP Authentication\n\n```javascript\nrequest.auth('username', 'password', false).get('http://some.server.com/');\n// or\nrequest.get('http://some.server.com/', {\n 'auth': {\n 'user': 'username',\n 'pass': 'password',\n 'sendImmediately': false\n }\n});\n```\n\nIf passed as an option, `auth` should be a hash containing values `user` || `username`, `password` || `pass`, and `sendImmediately` (optional). The method form takes parameters `auth(username, password, sendImmediately)`.\n\n`sendImmediately` defaults to true, which will cause a basic 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).\n\nDigest 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).\n\n## OAuth Signing\n\n```javascript\n// Twitter OAuth\nvar qs = require('querystring')\n , oauth =\n { callback: 'http://mysite.com/callback/'\n , consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n }\n , url = 'https://api.twitter.com/oauth/request_token'\n ;\nrequest.post({url:url, oauth:oauth}, function (e, r, body) {\n // Ideally, you would take the body in the response\n // and construct a URL that a user clicks on (like a sign in button).\n // The verifier is only available in the response after a user has \n // verified with twitter that they are authorizing your app.\n var access_token = qs.parse(body)\n , oauth = \n { consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n , token: access_token.oauth_token\n , verifier: access_token.oauth_verifier\n }\n , url = 'https://api.twitter.com/oauth/access_token'\n ;\n request.post({url:url, oauth:oauth}, function (e, r, body) {\n var perm_token = qs.parse(body)\n , oauth = \n { consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n , token: perm_token.oauth_token\n , token_secret: perm_token.oauth_token_secret\n }\n , url = 'https://api.twitter.com/1/users/show.json?'\n , params = \n { screen_name: perm_token.screen_name\n , user_id: perm_token.user_id\n }\n ;\n url += qs.stringify(params)\n request.get({url:url, oauth:oauth, json:true}, function (e, r, user) {\n console.log(user)\n })\n })\n})\n```\n\n\n\n### request(options, callback)\n\nThe first argument can be either a url or an options object. The only required option is uri, all others are optional.\n\n* `uri` || `url` - fully qualified uri or a parsed url object from url.parse()\n* `qs` - object containing querystring values to be appended to the uri\n* `method` - http method, defaults to GET\n* `headers` - http headers, defaults to {}\n* `body` - entity body for PATCH, POST and PUT requests. Must be buffer or string.\n* `form` - when passed an object this will set `body` but to a querystring representation of value and adds `Content-type: application/x-www-form-urlencoded; charset=utf-8` header. When passed no option a FormData instance is returned that will be piped to request.\n* `auth` - A hash containing values `user` || `username`, `password` || `pass`, and `sendImmediately` (optional). See documentation above.\n* `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json` header. Additionally, parses the response body as json.\n* `multipart` - (experimental) array of objects which contains their own headers and `body` attribute. Sends `multipart/related` request. See example below.\n* `followRedirect` - follow HTTP 3xx responses as redirects. defaults to true.\n* `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects. defaults to false.\n* `maxRedirects` - the maximum number of redirects to follow, defaults to 10.\n* `encoding` - Encoding to be used on `setEncoding` of response data. If set to `null`, the body is returned as a Buffer.\n* `pool` - A hash object containing the agents for these requests. If omitted this request will use the global pool which is set to node's default maxSockets.\n* `pool.maxSockets` - Integer containing the maximum amount of sockets in the pool.\n* `timeout` - Integer containing the number of milliseconds to wait for a request to respond before aborting the request\t\n* `proxy` - An HTTP proxy to be used. Support proxy Auth with Basic Auth the same way it's supported with the `url` parameter by embedding the auth info in the uri.\n* `oauth` - Options for OAuth HMAC-SHA1 signing, see documentation above.\n* `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).\n* `strictSSL` - Set to `true` to require that 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.\n* `jar` - Set to `false` if you don't want cookies to be remembered for future use or define your custom cookie jar (see examples section)\n* `aws` - object containing aws signing information, should have the properties `key` and `secret` as well as `bucket` unless you're specifying your bucket as part of the path, or you are making a request that doesn't use a bucket (i.e. GET Services)\n\n\nThe callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body String or Buffer.\n\n## Convenience methods\n\nThere are also shorthand methods for different HTTP METHODs and some other conveniences.\n\n### request.defaults(options) \n \nThis method returns a wrapper around the normal request API that defaults to whatever options you pass in to it.\n\n### request.put\n\nSame as request() but defaults to `method: \"PUT\"`.\n\n```javascript\nrequest.put(url)\n```\n\n### request.patch\n\nSame as request() but defaults to `method: \"PATCH\"`.\n\n```javascript\nrequest.patch(url)\n```\n\n### request.post\n\nSame as request() but defaults to `method: \"POST\"`.\n\n```javascript\nrequest.post(url)\n```\n\n### request.head\n\nSame as request() but defaults to `method: \"HEAD\"`.\n\n```javascript\nrequest.head(url)\n```\n\n### request.del\n\nSame as request() but defaults to `method: \"DELETE\"`.\n\n```javascript\nrequest.del(url)\n```\n\n### request.get\n\nAlias to normal request method for uniformity.\n\n```javascript\nrequest.get(url)\n```\n### request.cookie\n\nFunction that creates a new cookie.\n\n```javascript\nrequest.cookie('cookie_string_here')\n```\n### request.jar\n\nFunction that creates a new cookie jar.\n\n```javascript\nrequest.jar()\n```\n\n\n## Examples:\n\n```javascript\n var request = require('request')\n , rand = Math.floor(Math.random()*100000000).toString()\n ;\n request(\n { method: 'PUT'\n , uri: 'http://mikeal.iriscouch.com/testjs/' + rand\n , multipart: \n [ { 'content-type': 'application/json'\n , body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})\n }\n , { body: 'I am an attachment' }\n ] \n }\n , function (error, response, body) {\n if(response.statusCode == 201){\n console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand)\n } else {\n console.log('error: '+ response.statusCode)\n console.log(body)\n }\n }\n )\n```\nCookies are enabled by default (so they can be used in subsequent requests). To disable cookies set jar to false (either in defaults or in the options sent).\n\n```javascript\nvar request = request.defaults({jar: false})\nrequest('http://www.google.com', function () {\n request('http://images.google.com')\n})\n```\n\nIf you to use a custom cookie jar (instead of letting request use its own global cookie jar) you do so by setting the jar default or by specifying it as an option:\n\n```javascript\nvar j = request.jar()\nvar request = request.defaults({jar:j})\nrequest('http://www.google.com', function () {\n request('http://images.google.com')\n})\n```\nOR\n\n```javascript\nvar j = request.jar()\nvar cookie = request.cookie('your_cookie_here')\nj.add(cookie)\nrequest({url: 'http://www.google.com', jar: j}, function () {\n request('http://images.google.com')\n})\n```\n",
+ "readme": "# Request -- Simplified HTTP request method\n\n## Install\n\n<pre>\n npm install request\n</pre>\n\nOr from source:\n\n<pre>\n git clone git://github.com/mikeal/request.git \n cd request\n npm link\n</pre>\n\n## Super simple to use\n\nRequest is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.\n\n```javascript\nvar request = require('request');\nrequest('http://www.google.com', function (error, response, body) {\n if (!error && response.statusCode == 200) {\n console.log(body) // Print the google web page.\n }\n})\n```\n\n## Streaming\n\nYou can stream any response to a file stream.\n\n```javascript\nrequest('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))\n```\n\nYou 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 one is not already provided in the headers.\n\n```javascript\nfs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))\n```\n\nRequest can also pipe to itself. When doing so the content-type and content-length will be preserved in the PUT headers.\n\n```javascript\nrequest.get('http://google.com/img.png').pipe(request.put('http://mysite.com/img.png'))\n```\n\nNow let's get fancy.\n\n```javascript\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n if (req.method === 'PUT') {\n req.pipe(request.put('http://mysite.com/doodle.png'))\n } else if (req.method === 'GET' || req.method === 'HEAD') {\n request.get('http://mysite.com/doodle.png').pipe(resp)\n } \n }\n})\n```\n\nYou can also pipe() from a http.ServerRequest instance and to a http.ServerResponse instance. The HTTP method and headers will be sent as well as the entity-body data. Which means that, if you don't really care about security, you can do:\n\n```javascript\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n var x = request('http://mysite.com/doodle.png')\n req.pipe(x)\n x.pipe(resp)\n }\n})\n```\n\nAnd since pipe() returns the destination stream in node 0.5.x you can do one line proxying :)\n\n```javascript\nreq.pipe(request('http://mysite.com/doodle.png')).pipe(resp)\n```\n\nAlso, none of this new functionality conflicts with requests previous features, it just expands them.\n\n```javascript\nvar r = request.defaults({'proxy':'http://localproxy.com'})\n\nhttp.createServer(function (req, resp) {\n if (req.url === '/doodle.png') {\n r.get('http://google.com/doodle.png').pipe(resp)\n }\n})\n```\nYou can still use intermediate proxies, the requests will still follow HTTP forwards, etc.\n\n## Forms\n\n`request` supports `application/x-www-form-urlencoded` and `multipart/form-data` form uploads. For `multipart/related` refer to the `multipart` API.\n\nUrl encoded forms are simple\n\n```javascript\nrequest.post('http://service.com/upload', {form:{key:'value'}})\n// or\nrequest.post('http://service.com/upload').form({key:'value'})\n```\n\nFor `multipart/form-data` we use the [form-data](https://github.com/felixge/node-form-data) library by [@felixge](https://github.com/felixge). You don't need to worry about piping the form object or setting the headers, `request` will handle that for you.\n\n```javascript\nvar r = request.post('http://service.com/upload')\nvar form = r.form()\nform.append('my_field', 'my_value')\nform.append('my_buffer', new Buffer([1, 2, 3]))\nform.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png'))\nform.append('remote_file', request('http://google.com/doodle.png'))\n```\n\n## HTTP Authentication\n\n```javascript\nrequest.auth('username', 'password', false).get('http://some.server.com/');\n// or\nrequest.get('http://some.server.com/', {\n 'auth': {\n 'user': 'username',\n 'pass': 'password',\n 'sendImmediately': false\n }\n});\n```\n\nIf passed as an option, `auth` should be a hash containing values `user` || `username`, `password` || `pass`, and `sendImmediately` (optional). The method form takes parameters `auth(username, password, sendImmediately)`.\n\n`sendImmediately` defaults to true, which will cause a basic 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).\n\nDigest 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).\n\n## OAuth Signing\n\n```javascript\n// Twitter OAuth\nvar qs = require('querystring')\n , oauth =\n { callback: 'http://mysite.com/callback/'\n , consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n }\n , url = 'https://api.twitter.com/oauth/request_token'\n ;\nrequest.post({url:url, oauth:oauth}, function (e, r, body) {\n // Ideally, you would take the body in the response\n // and construct a URL that a user clicks on (like a sign in button).\n // The verifier is only available in the response after a user has \n // verified with twitter that they are authorizing your app.\n var access_token = qs.parse(body)\n , oauth = \n { consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n , token: access_token.oauth_token\n , verifier: access_token.oauth_verifier\n }\n , url = 'https://api.twitter.com/oauth/access_token'\n ;\n request.post({url:url, oauth:oauth}, function (e, r, body) {\n var perm_token = qs.parse(body)\n , oauth = \n { consumer_key: CONSUMER_KEY\n , consumer_secret: CONSUMER_SECRET\n , token: perm_token.oauth_token\n , token_secret: perm_token.oauth_token_secret\n }\n , url = 'https://api.twitter.com/1/users/show.json?'\n , params = \n { screen_name: perm_token.screen_name\n , user_id: perm_token.user_id\n }\n ;\n url += qs.stringify(params)\n request.get({url:url, oauth:oauth, json:true}, function (e, r, user) {\n console.log(user)\n })\n })\n})\n```\n\n\n\n### request(options, callback)\n\nThe first argument can be either a url or an options object. The only required option is uri, all others are optional.\n\n* `uri` || `url` - fully qualified uri or a parsed url object from url.parse()\n* `qs` - object containing querystring values to be appended to the uri\n* `method` - http method, defaults to GET\n* `headers` - http headers, defaults to {}\n* `body` - entity body for PATCH, POST and PUT requests. Must be buffer or string.\n* `form` - when passed an object this will set `body` but to a querystring representation of value and adds `Content-type: application/x-www-form-urlencoded; charset=utf-8` header. When passed no option a FormData instance is returned that will be piped to request.\n* `auth` - A hash containing values `user` || `username`, `password` || `pass`, and `sendImmediately` (optional). See documentation above.\n* `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json` header. Additionally, parses the response body as json.\n* `multipart` - (experimental) array of objects which contains their own headers and `body` attribute. Sends `multipart/related` request. See example below.\n* `followRedirect` - follow HTTP 3xx responses as redirects. defaults to true.\n* `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects. defaults to false.\n* `maxRedirects` - the maximum number of redirects to follow, defaults to 10.\n* `encoding` - Encoding to be used on `setEncoding` of response data. If set to `null`, the body is returned as a Buffer.\n* `pool` - A hash object containing the agents for these requests. If omitted this request will use the global pool which is set to node's default maxSockets.\n* `pool.maxSockets` - Integer containing the maximum amount of sockets in the pool.\n* `timeout` - Integer containing the number of milliseconds to wait for a request to respond before aborting the request\t\n* `proxy` - An HTTP proxy to be used. Support proxy Auth with Basic Auth the same way it's supported with the `url` parameter by embedding the auth info in the uri.\n* `oauth` - Options for OAuth HMAC-SHA1 signing, see documentation above.\n* `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).\n* `strictSSL` - Set to `true` to require that 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.\n* `jar` - Set to `false` if you don't want cookies to be remembered for future use or define your custom cookie jar (see examples section)\n* `aws` - object containing aws signing information, should have the properties `key` and `secret` as well as `bucket` unless you're specifying your bucket as part of the path, or you are making a request that doesn't use a bucket (i.e. GET Services)\n* `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.\n* `localAddress` - Local interface to bind for network connections.\n\n\nThe callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body String or Buffer.\n\n## Convenience methods\n\nThere are also shorthand methods for different HTTP METHODs and some other conveniences.\n\n### request.defaults(options) \n \nThis method returns a wrapper around the normal request API that defaults to whatever options you pass in to it.\n\n### request.put\n\nSame as request() but defaults to `method: \"PUT\"`.\n\n```javascript\nrequest.put(url)\n```\n\n### request.patch\n\nSame as request() but defaults to `method: \"PATCH\"`.\n\n```javascript\nrequest.patch(url)\n```\n\n### request.post\n\nSame as request() but defaults to `method: \"POST\"`.\n\n```javascript\nrequest.post(url)\n```\n\n### request.head\n\nSame as request() but defaults to `method: \"HEAD\"`.\n\n```javascript\nrequest.head(url)\n```\n\n### request.del\n\nSame as request() but defaults to `method: \"DELETE\"`.\n\n```javascript\nrequest.del(url)\n```\n\n### request.get\n\nAlias to normal request method for uniformity.\n\n```javascript\nrequest.get(url)\n```\n### request.cookie\n\nFunction that creates a new cookie.\n\n```javascript\nrequest.cookie('cookie_string_here')\n```\n### request.jar\n\nFunction that creates a new cookie jar.\n\n```javascript\nrequest.jar()\n```\n\n\n## Examples:\n\n```javascript\n var request = require('request')\n , rand = Math.floor(Math.random()*100000000).toString()\n ;\n request(\n { method: 'PUT'\n , uri: 'http://mikeal.iriscouch.com/testjs/' + rand\n , multipart: \n [ { 'content-type': 'application/json'\n , body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})\n }\n , { body: 'I am an attachment' }\n ] \n }\n , function (error, response, body) {\n if(response.statusCode == 201){\n console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand)\n } else {\n console.log('error: '+ response.statusCode)\n console.log(body)\n }\n }\n )\n```\nCookies are enabled by default (so they can be used in subsequent requests). To disable cookies set jar to false (either in defaults or in the options sent).\n\n```javascript\nvar request = request.defaults({jar: false})\nrequest('http://www.google.com', function () {\n request('http://images.google.com')\n})\n```\n\nIf you to use a custom cookie jar (instead of letting request use its own global cookie jar) you do so by setting the jar default or by specifying it as an option:\n\n```javascript\nvar j = request.jar()\nvar request = request.defaults({jar:j})\nrequest('http://www.google.com', function () {\n request('http://images.google.com')\n})\n```\nOR\n\n```javascript\nvar j = request.jar()\nvar cookie = request.cookie('your_cookie_here')\nj.add(cookie)\nrequest({url: 'http://www.google.com', jar: j}, function () {\n request('http://images.google.com')\n})\n```\n",
"readmeFilename": "README.md",
- "_id": "request@2.16.6",
+ "_id": "request@2.21.0",
"_from": "request@latest"
}
if (req.headers.authorization) {
if (req.headers.authorization == 'Basic ' + new Buffer('test:testing2').toString('base64')) {
ok = true;
+ } else if ( req.headers.authorization == 'Basic ' + new Buffer(':apassword').toString('base64')) {
+ ok = true;
} else {
// Bad auth header, don't send back WWW-Authenticate header
ok = false;
res.setHeader('www-authenticate', 'Basic realm="Private"');
}
+ if (req.url == '/post/') {
+ var expectedContent = 'data_key=data_value';
+ req.on('data', function(data) {
+ assert.equal(data, expectedContent);
+ console.log('received request data: ' + data);
+ });
+ assert.equal(req.method, 'POST');
+ assert.equal(req.headers['content-length'], '' + expectedContent.length);
+ assert.equal(req.headers['content-type'], 'application/x-www-form-urlencoded; charset=utf-8');
+ }
+
if (ok) {
console.log('request ok');
res.end('ok');
basicServer.listen(6767);
-request({
- 'method': 'GET',
- 'uri': 'http://localhost:6767/test/',
- 'auth': {
- 'user': 'test',
- 'pass': 'testing2',
- 'sendImmediately': false
- }
-}, function(error, response, body) {
- assert.equal(response.statusCode, 200);
- assert.equal(numBasicRequests, 2);
+var tests = [
+ function(next) {
+ request({
+ 'method': 'GET',
+ 'uri': 'http://localhost:6767/test/',
+ 'auth': {
+ 'user': 'test',
+ 'pass': 'testing2',
+ 'sendImmediately': false
+ }
+ }, function(error, res, body) {
+ assert.equal(res.statusCode, 200);
+ assert.equal(numBasicRequests, 2);
+ next();
+ });
+ },
- // If we don't set sendImmediately = false, request will send basic auth
- request({
- 'method': 'GET',
- 'uri': 'http://localhost:6767/test2/',
- 'auth': {
- 'user': 'test',
- 'pass': 'testing2'
- }
- }, function(error, response, body) {
- assert.equal(response.statusCode, 200);
- assert.equal(numBasicRequests, 3);
+ function(next) {
+ // If we don't set sendImmediately = false, request will send basic auth
+ request({
+ 'method': 'GET',
+ 'uri': 'http://localhost:6767/test2/',
+ 'auth': {
+ 'user': 'test',
+ 'pass': 'testing2'
+ }
+ }, function(error, res, body) {
+ assert.equal(res.statusCode, 200);
+ assert.equal(numBasicRequests, 3);
+ next();
+ });
+ },
+ function(next) {
request({
'method': 'GET',
'uri': 'http://test:testing2@localhost:6767/test2/'
- }, function(error, response, body) {
- assert.equal(response.statusCode, 200);
+ }, function(error, res, body) {
+ assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 4);
+ next();
+ });
+ },
- console.log('All tests passed');
- basicServer.close();
+ function(next) {
+ request({
+ 'method': 'POST',
+ 'form': { 'data_key': 'data_value' },
+ 'uri': 'http://localhost:6767/post/',
+ 'auth': {
+ 'user': 'test',
+ 'pass': 'testing2',
+ 'sendImmediately': false
+ }
+ }, function(error, res, body) {
+ assert.equal(res.statusCode, 200);
+ assert.equal(numBasicRequests, 6);
+ next();
});
- });
-});
+ },
+
+ function(next) {
+ assert.doesNotThrow( function() {
+ request({
+ 'method': 'GET',
+ 'uri': 'http://localhost:6767/allow_empty_user/',
+ 'auth': {
+ 'user': '',
+ 'pass': 'apassword',
+ 'sendImmediately': false
+ }
+ }, function(error, res, body ) {
+ assert.equal(res.statusCode, 200);
+ assert.equal(numBasicRequests, 8);
+ next();
+ });
+ })
+ }
+];
+
+function runTest(i) {
+ if (i < tests.length) {
+ tests[i](function() {
+ runTest(i + 1);
+ });
+ } else {
+ console.log('All tests passed');
+ basicServer.close();
+ }
+}
+
+runTest(0);
--- /dev/null
+var request = require('../index')
+ , http = require('http')
+ , assert = require('assert')
+ ;
+
+var s = http.createServer(function (req, resp) {
+ resp.statusCode = 200
+ resp.end('')
+}).listen(8080, function () {
+ var r = request('http://localhost:8080', function (e, resp, body) {
+ assert.equal(resp.statusCode, 200)
+ assert.equal(body, "")
+
+ var r2 = request({ url: 'http://localhost:8080', json: {} }, function (e, resp, body) {
+ assert.equal(resp.statusCode, 200)
+ assert.equal(body, undefined)
+ s.close()
+ });
+ })
+})
--- /dev/null
+var createServer = require('http').createServer
+ , request = require('../index')
+ , httpSignature = require('http-signature')
+ , assert = require('assert')
+ ;
+
+var privateKeyPEMs = {}
+
+privateKeyPEMs['key-1'] =
+ '-----BEGIN RSA PRIVATE KEY-----\n' +
+ 'MIIEpAIBAAKCAQEAzWSJl+Z9Bqv00FVL5N3+JCUoqmQPjIlya1BbeqQroNQ5yG1i\n' +
+ 'VbYTTnMRa1zQtR6r2fNvWeg94DvxivxIG9diDMnrzijAnYlTLOl84CK2vOxkj5b6\n' +
+ '8zrLH9b/Gd6NOHsywo8IjvXvCeTfca5WUHcuVi2lT9VjygFs1ILG4RyeX1BXUumu\n' +
+ 'Y8fzmposxLYdMxCqUTzAn0u9Saq2H2OVj5u114wS7OQPigu6G99dpn/iPHa3zBm8\n' +
+ '7baBWDbqZWRW0BP3K6eqq8sut1+NLhNW8ADPTdnO/SO+kvXy7fqd8atSn+HlQcx6\n' +
+ 'tW42dhXf3E9uE7K78eZtW0KvfyNGAjsI1Fft2QIDAQABAoIBAG1exe3/LEBrPLfb\n' +
+ 'U8iRdY0lxFvHYIhDgIwohC3wUdMYb5SMurpNdEZn+7Sh/fkUVgp/GKJViu1mvh52\n' +
+ 'bKd2r52DwG9NQBQjVgkqY/auRYSglIPpr8PpYNSZlcneunCDGeqEY9hMmXc5Ssqs\n' +
+ 'PQYoEKKPN+IlDTg6PguDgAfLR4IUvt9KXVvmB/SSgV9tSeTy35LECt1Lq3ozbUgu\n' +
+ '30HZI3U6/7H+X22Pxxf8vzBtzkg5rRCLgv+OeNPo16xMnqbutt4TeqEkxRv5rtOo\n' +
+ '/A1i9khBeki0OJAFJsE82qnaSZodaRsxic59VnN8sWBwEKAt87tEu5A3K3j4XSDU\n' +
+ '/avZxAECgYEA+pS3DvpiQLtHlaO3nAH6MxHRrREOARXWRDe5nUQuUNpS1xq9wte6\n' +
+ 'DkFtba0UCvDLic08xvReTCbo9kH0y6zEy3zMpZuJlKbcWCkZf4S5miYPI0RTZtF8\n' +
+ 'yps6hWqzYFSiO9hMYws9k4OJLxX0x3sLK7iNZ32ujcSrkPBSiBr0gxkCgYEA0dWl\n' +
+ '637K41AJ/zy0FP0syq+r4eIkfqv+/t6y2aQVUBvxJYrj9ci6XHBqoxpDV8lufVYj\n' +
+ 'fUAfeI9/MZaWvQJRbnYLre0I6PJfLuCBIL5eflO77BGso165AF7QJZ+fwtgKv3zv\n' +
+ 'ZX75eudCSS/cFo0po9hlbcLMT4B82zEkgT8E2MECgYEAnz+3/wrdOmpLGiyL2dff\n' +
+ '3GjsqmJ2VfY8z+niSrI0BSpbD11tT9Ct67VlCBjA7hsOH6uRfpd6/kaUMzzDiFVq\n' +
+ 'VDAiFvV8QD6zNkwYalQ9aFvbrvwTTPrBpjl0vamMCiJ/YC0cjq1sGr2zh3sar1Ph\n' +
+ 'S43kP+s97dcZeelhaiJHVrECgYEAsx61q/loJ/LDFeYzs1cLTVn4V7I7hQY9fkOM\n' +
+ 'WM0AhInVqD6PqdfXfeFYpjJdGisQ7l0BnoGGW9vir+nkcyPvb2PFRIr6+B8tsU5j\n' +
+ '7BeVgjDoUfQkcrEBK5fEBtnj/ud9BUkY8oMZZBjVNLRuI7IMwZiPvMp0rcj4zAN/\n' +
+ 'LfUlpgECgYArBvFcBxSkNAzR3Rtteud1YDboSKluRM37Ey5plrn4BS0DD0jm++aD\n' +
+ '0pG2Hsik000hibw92lCkzvvBVAqF8BuAcnPlAeYfsOaa97PGEjSKEN5bJVWZ9/om\n' +
+ '9FV1axotRN2XWlwrhixZLEaagkREXhgQc540FS5O8IaI2Vpa80Atzg==\n' +
+ '-----END RSA PRIVATE KEY-----'
+
+var publicKeyPEMs = {}
+
+publicKeyPEMs['key-1'] =
+ '-----BEGIN PUBLIC KEY-----\n' +
+ 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzWSJl+Z9Bqv00FVL5N3+\n' +
+ 'JCUoqmQPjIlya1BbeqQroNQ5yG1iVbYTTnMRa1zQtR6r2fNvWeg94DvxivxIG9di\n' +
+ 'DMnrzijAnYlTLOl84CK2vOxkj5b68zrLH9b/Gd6NOHsywo8IjvXvCeTfca5WUHcu\n' +
+ 'Vi2lT9VjygFs1ILG4RyeX1BXUumuY8fzmposxLYdMxCqUTzAn0u9Saq2H2OVj5u1\n' +
+ '14wS7OQPigu6G99dpn/iPHa3zBm87baBWDbqZWRW0BP3K6eqq8sut1+NLhNW8ADP\n' +
+ 'TdnO/SO+kvXy7fqd8atSn+HlQcx6tW42dhXf3E9uE7K78eZtW0KvfyNGAjsI1Fft\n' +
+ '2QIDAQAB\n' +
+ '-----END PUBLIC KEY-----'
+
+publicKeyPEMs['key-2'] =
+ '-----BEGIN PUBLIC KEY-----\n' +
+ 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqp04VVr9OThli9b35Omz\n' +
+ 'VqSfWbsoQuRrgyWsrNRn3XkFmbWw4FzZwQ42OgGMzQ84Ta4d9zGKKQyFriTiPjPf\n' +
+ 'xhhrsaJnDuybcpVhcr7UNKjSZ0S59tU3hpRiEz6hO+Nc/OSSLkvalG0VKrxOln7J\n' +
+ 'LK/h3rNS/l6wDZ5S/KqsI6CYtV2ZLpn3ahLrizvEYNY038Qcm38qMWx+VJAvZ4di\n' +
+ 'qqmW7RLIsLT59SWmpXdhFKnkYYGhxrk1Mwl22dBTJNY5SbriU5G3gWgzYkm8pgHr\n' +
+ '6CtrXch9ciJAcDJehPrKXNvNDOdUh8EW3fekNJerF1lWcwQg44/12v8sDPyfbaKB\n' +
+ 'dQIDAQAB\n' +
+ '-----END PUBLIC KEY-----'
+
+var server = createServer(function (req, res) {
+ var parsed = httpSignature.parseRequest(req)
+ var publicKeyPEM = publicKeyPEMs[parsed.keyId]
+ var verified = httpSignature.verifySignature(parsed, publicKeyPEM)
+ res.writeHead(verified ? 200 : 400)
+ res.end()
+})
+
+server.listen(8080, function () {
+ function correctKeyTest(callback) {
+ var options = {
+ httpSignature: {
+ keyId: 'key-1',
+ key: privateKeyPEMs['key-1']
+ }
+ }
+ request('http://localhost:8080', options, function (e, r, b) {
+ assert.equal(200, r.statusCode)
+ callback()
+ })
+ }
+
+ function incorrectKeyTest(callback) {
+ var options = {
+ httpSignature: {
+ keyId: 'key-2',
+ key: privateKeyPEMs['key-1']
+ }
+ }
+ request('http://localhost:8080', options, function (e, r, b) {
+ assert.equal(400, r.statusCode)
+ callback()
+ })
+ }
+
+ var tests = [correctKeyTest, incorrectKeyTest]
+ var todo = tests.length;
+ for(var i = 0; i < tests.length; ++i) {
+ tests[i](function() {
+ if(!--todo) {
+ server.close()
+ }
+ })
+ }
+})
--- /dev/null
+var request = require('../index')
+ , assert = require('assert')
+ ;
+
+request.get({
+ uri: 'http://www.google.com', localAddress: '1.2.3.4' // some invalid address
+}, function(err, res) {
+ assert(!res) // asserting that no response received
+})
+
+request.get({
+ uri: 'http://www.google.com', localAddress: '127.0.0.1'
+}, function(err, res) {
+ assert(!res) // asserting that no response received
+})
--- /dev/null
+var http = require('http')
+ , assert = require('assert')
+ , request = require('../index')
+ ;
+
+var server = http.createServer(function (req, resp) {
+ resp.statusCode = 200
+ if (req.url === '/get') {
+ assert.equal(req.method, 'GET')
+ resp.write('content')
+ resp.end()
+ return
+ }
+ if (req.url === '/put') {
+ var x = ''
+ assert.equal(req.method, 'PUT')
+ req.on('data', function (chunk) {
+ x += chunk
+ })
+ req.on('end', function () {
+ assert.equal(x, 'content')
+ resp.write('success')
+ resp.end()
+ })
+ return
+ }
+ if (req.url === '/proxy') {
+ assert.equal(req.method, 'PUT')
+ return req.pipe(request('http://localhost:8080/put')).pipe(resp)
+ }
+
+ if (req.url === '/test') {
+ return request('http://localhost:8080/get').pipe(request.put('http://localhost:8080/proxy')).pipe(resp)
+ }
+ throw new Error('Unknown url', req.url)
+}).listen(8080, function () {
+ request('http://localhost:8080/test', function (e, resp, body) {
+ if (e) throw e
+ if (resp.statusCode !== 200) throw new Error('statusCode not 200 ' + resp.statusCode)
+ assert.equal(body, 'success')
+ server.close()
+ })
+})
+
+
+
setTimeout(function(){
assert.equal('/', req5.path)
}, 1)
+
+
+// Test modifying the qs after creating the request
+var req6 = request.get({ uri: 'http://www.google.com', qs: {}});
+req6.qs({ q: "test" });
+process.nextTick(function() {
+ assert.equal('/?q=test', req6.path);
+});
.join('\nSQUIDOUT '))
})
+squid.on('error', function (c) {
+ console.error('squid: error '+c)
+ if (c && !ready) {
+ notInstalled()
+ return
+ }
+})
+
squid.on('exit', function (c) {
console.error('squid: exit '+c)
if (c && !ready) {
- console.error('squid must be installed to run this test.')
- console.error('skipping this test. please install squid and run again if you need to test tunneling.')
- c = null
- hadError = null
- process.exit(0)
+ notInstalled()
return
}
squid.kill('SIGKILL')
})
}, 100)
+
+function notInstalled() {
+ console.error('squid must be installed to run this test.')
+ console.error('skipping this test. please install squid and run again if you need to test tunneling.')
+ c = null
+ hadError = null
+ process.exit(0)
+}
--- /dev/null
+node_modules
+test
+.gitignore
+.travis.yml
\ No newline at end of file
--- /dev/null
+# sha
+
+Check and get file hashes (using any algorithm)
+
+[![Build Status](https://travis-ci.org/ForbesLindesay/sha.png?branch=master)](https://travis-ci.org/ForbesLindesay/sha)
+[![Dependency Status](https://gemnasium.com/ForbesLindesay/sha.png)](https://gemnasium.com/ForbesLindesay/sha)
+
+## Installation
+
+ $ npm install sha
+
+## API
+
+### check(fileName, expected, [options,] cb)
+
+Asynchronously check that `fileName` has a "hash" of `expected`. The callback will be called with either `null` or an error (indicating that they did not match).
+
+Options:
+
+- algorithm: defaults to `sha1` and can be any of the algorithms supported by `crypto.createHash`
+
+### get(fileName, [options,] cb)
+
+Asynchronously get the "hash" of `fileName`. The callback will be called with an optional `error` object and the (lower cased) hex digest of the hash.
+
+Options:
+
+- algorithm: defaults to `sha1` and can be any of the algorithms supported by `crypto.createHash`
+
+## License
+
+BSD
--- /dev/null
+var crypto = require('crypto')
+var fs
+try {
+ fs = require('graceful-fs')
+} catch (ex) {
+ fs = require('fs')
+}
+try {
+ process.binding('crypto')
+} catch (e) {
+ var er = new Error( 'crypto binding not found.\n'
+ + 'Please build node with openssl.\n'
+ + e.message )
+ throw er
+}
+
+exports.check = check
+exports.get = get
+
+function check(file, expected, options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = undefined;
+ }
+ expected = expected.toLowerCase().trim();
+ get(file, options, function (er, actual) {
+ if (er) {
+ if (er.message) er.message += ' while getting shasum for ' + file;
+ return cb(er)
+ }
+ if (actual === expected) return cb(null);
+ cb(new Error(
+ 'shasum check failed for ' + file + '\n'
+ + 'Expected: ' + expected + '\n'
+ + 'Actual: ' + actual))
+ })
+}
+
+function get(file, options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = undefined;
+ }
+ options = options || {};
+ var algorithm = options.algorithm || 'sha1';
+ var hash = crypto.createHash(algorithm)
+ var source = fs.createReadStream(file)
+ var errState = null
+ source
+ .on('error', function (er) {
+ if (errState) return
+ return cb(errState = er)
+ })
+ .on('data', function (chunk) {
+ if (errState) return
+ hash.update(chunk)
+ })
+ .on('end', function () {
+ if (errState) return
+ var actual = hash.digest("hex").toLowerCase().trim()
+ cb(null, actual)
+ })
+}
--- /dev/null
+{
+ "name": "sha",
+ "version": "1.0.1",
+ "description": "Check and get file hashes",
+ "scripts": {
+ "test": "mocha -R list"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ForbesLindesay/sha.git"
+ },
+ "license": "BSD",
+ "optionalDependencies": {
+ "graceful-fs": "1.2"
+ },
+ "devDependencies": {
+ "mocha": "~1.9.0"
+ },
+ "readme": "# sha\r\n\r\nCheck and get file hashes (using any algorithm)\r\n\r\n[![Build Status](https://travis-ci.org/ForbesLindesay/sha.png?branch=master)](https://travis-ci.org/ForbesLindesay/sha)\r\n[![Dependency Status](https://gemnasium.com/ForbesLindesay/sha.png)](https://gemnasium.com/ForbesLindesay/sha)\r\n\r\n## Installation\r\n\r\n $ npm install sha\r\n\r\n## API\r\n\r\n### check(fileName, expected, [options,] cb)\r\n\r\nAsynchronously check that `fileName` has a \"hash\" of `expected`. The callback will be called with either `null` or an error (indicating that they did not match).\r\n\r\nOptions:\r\n\r\n- algorithm: defaults to `sha1` and can be any of the algorithms supported by `crypto.createHash`\r\n\r\n### get(fileName, [options,] cb)\r\n\r\nAsynchronously get the \"hash\" of `fileName`. The callback will be called with an optional `error` object and the (lower cased) hex digest of the hash.\r\n\r\nOptions:\r\n\r\n- algorithm: defaults to `sha1` and can be any of the algorithms supported by `crypto.createHash`\r\n\r\n## License\r\n\r\nBSD\r\n",
+ "readmeFilename": "README.md",
+ "_id": "sha@1.0.1",
+ "dependencies": {
+ "graceful-fs": "1.2"
+ },
+ "_from": "sha@~1.0.1"
+}
-# Slide - a tiny flow control library
+# Controlling Flow: callbacks are easy
-Callbacks are simple and easy if you keep the pattern consistent.
+## What's actually hard?
-Check out the [slide
-presentation](http://github.com/isaacs/slide-flow-control/raw/master/nodejs-controlling-flow.pdf),
-or the [blog post](http://howto.no.de/flow-control-in-npm).
+- Doing a bunch of things in a specific order.
+- Knowing when stuff is done.
+- Handling failures.
+- Breaking up functionality into parts (avoid nested inline callbacks)
-You'll laugh when you see how little code is actually in this thing.
-It's so not-enterprisey, you won't believe it. It does almost nothing,
-but it's super handy.
-I use this util in [a real world program](http://npmjs.org/).
+## Common Mistakes
-You should use it as an example of how to write your own flow control
-utilities. You'll never fully appreciate a flow control lib that you
-didn't write yourself.
+- Abandoning convention and consistency.
+- Putting all callbacks inline.
+- Using libraries without grokking them.
+- Trying to make async code look sync.
-## Installation
+## Define Conventions
-Just copy the files into your project, and use them that way, or
-you can do this:
+- Two kinds of functions: *actors* take action, *callbacks* get results.
+- Essentially the continuation pattern. Resulting code *looks* similar
+ to fibers, but is *much* simpler to implement.
+- Node works this way in the lowlevel APIs already, and it's very flexible.
- npm install slide
+## Callbacks
-and then:
+- Simple responders
+- Must always be prepared to handle errors, that's why it's the first argument.
+- Often inline anonymous, but not always.
+- Can trap and call other callbacks with modified data, or pass errors upwards.
- var asyncMap = require("slide").asyncMap
- , chain = require("slide").chain
- // use the power!
+## Actors
-Enjoy!
+- Last argument is a callback.
+- If any error occurs, and can't be handled, pass it to the callback and return.
+- Must not throw. Return value ignored.
+- return x ==> return cb(null, x)
+- throw er ==> return cb(er)
+
+```javascript
+// return true if a path is either
+// a symlink or a directory.
+function isLinkOrDir (path, cb) {
+ fs.lstat(path, function (er, s) {
+ if (er) return cb(er)
+ return cb(null, s.isDirectory() || s.isSymbolicLink())
+ })
+}
+```
+
+# asyncMap
+
+## Usecases
+
+- I have a list of 10 files, and need to read all of them, and then continue when they're all done.
+- I have a dozen URLs, and need to fetch them all, and then continue when they're all done.
+- I have 4 connected users, and need to send a message to all of them, and then continue when that's done.
+- I have a list of n things, and I need to dosomething with all of them, in parallel, and get the results once they're all complete.
+
+
+## Solution
+
+```javascript
+var asyncMap = require("slide").asyncMap
+function writeFiles (files, what, cb) {
+ asyncMap(files, function (f, cb) {
+ fs.writeFile(f, what, cb)
+ }, cb)
+}
+writeFiles([my, file, list], "foo", cb)
+```
+
+# chain
+
+## Usecases
+
+- I have to do a bunch of things, in order. Get db credentials out of a file,
+ read the data from the db, write that data to another file.
+- If anything fails, do not continue.
+- I still have to provide an array of functions, which is a lot of boilerplate,
+ and a pita if your functions take args like
+
+```javascript
+function (cb) {
+ blah(a, b, c, cb)
+}
+```
+
+- Results are discarded, which is a bit lame.
+- No way to branch.
+
+## Solution
+
+- reduces boilerplate by converting an array of [fn, args] to an actor
+ that takes no arguments (except cb)
+- A bit like Function#bind, but tailored for our use-case.
+- bindActor(obj, "method", a, b, c)
+- bindActor(fn, a, b, c)
+- bindActor(obj, fn, a, b, c)
+- branching, skipping over falsey arguments
+
+```javascript
+chain([
+ doThing && [thing, a, b, c]
+, isFoo && [doFoo, "foo"]
+, subChain && [chain, [one, two]]
+], cb)
+```
+
+- tracking results: results are stored in an optional array passed as argument,
+ last result is always in results[results.length - 1].
+- treat chain.first and chain.last as placeholders for the first/last
+ result up until that point.
+
+
+## Non-trivial example
+
+- Read number files in a directory
+- Add the results together
+- Ping a web service with the result
+- Write the response to a file
+- Delete the number files
+
+```javascript
+var chain = require("slide").chain
+function myProgram (cb) {
+ var res = [], last = chain.last, first = chain.first
+ chain([
+ [fs, "readdir", "the-directory"]
+ , [readFiles, "the-directory", last]
+ , [sum, last]
+ , [ping, "POST", "example.com", 80, "/foo", last]
+ , [fs, "writeFile", "result.txt", last]
+ , [rmFiles, "./the-directory", first]
+ ], res, cb)
+}
+```
+
+# Conclusion: Convention Profits
+
+- Consistent API from top to bottom.
+- Sneak in at any point to inject functionality. Testable, reusable, ...
+- When ruby and python users whine, you can smile condescendingly.
{
"name": "slide",
- "version": "1.1.3",
- "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
+ "version": "1.1.4",
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
"contributors": [
- "S. Sriram <ssriram@gmail.com> (http://www.565labs.com)"
+ {
+ "name": "S. Sriram",
+ "email": "ssriram@gmail.com",
+ "url": "http://www.565labs.com"
+ }
],
"description": "A flow control lib small enough to fit on in a slide presentation. Derived live at Oak.JS",
"main": "./lib/slide.js",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/slide-flow-control.git"
- }
+ },
+ "readme": "# Controlling Flow: callbacks are easy\n\n## What's actually hard?\n\n- Doing a bunch of things in a specific order.\n- Knowing when stuff is done.\n- Handling failures.\n- Breaking up functionality into parts (avoid nested inline callbacks)\n\n\n## Common Mistakes\n\n- Abandoning convention and consistency.\n- Putting all callbacks inline.\n- Using libraries without grokking them.\n- Trying to make async code look sync.\n\n## Define Conventions\n\n- Two kinds of functions: *actors* take action, *callbacks* get results.\n- Essentially the continuation pattern. Resulting code *looks* similar\n to fibers, but is *much* simpler to implement.\n- Node works this way in the lowlevel APIs already, and it's very flexible.\n\n## Callbacks\n\n- Simple responders\n- Must always be prepared to handle errors, that's why it's the first argument.\n- Often inline anonymous, but not always.\n- Can trap and call other callbacks with modified data, or pass errors upwards.\n\n## Actors\n\n- Last argument is a callback.\n- If any error occurs, and can't be handled, pass it to the callback and return.\n- Must not throw. Return value ignored.\n- return x ==> return cb(null, x)\n- throw er ==> return cb(er)\n\n```javascript\n// return true if a path is either\n// a symlink or a directory.\nfunction isLinkOrDir (path, cb) {\n fs.lstat(path, function (er, s) {\n if (er) return cb(er)\n return cb(null, s.isDirectory() || s.isSymbolicLink())\n })\n}\n```\n\n# asyncMap\n\n## Usecases\n\n- I have a list of 10 files, and need to read all of them, and then continue when they're all done.\n- I have a dozen URLs, and need to fetch them all, and then continue when they're all done.\n- I have 4 connected users, and need to send a message to all of them, and then continue when that's done.\n- I have a list of n things, and I need to dosomething with all of them, in parallel, and get the results once they're all complete.\n\n\n## Solution\n\n```javascript\nvar asyncMap = require(\"slide\").asyncMap\nfunction writeFiles (files, what, cb) {\n asyncMap(files, function (f, cb) {\n fs.writeFile(f, what, cb)\n }, cb)\n}\nwriteFiles([my, file, list], \"foo\", cb)\n```\n\n# chain\n\n## Usecases\n\n- I have to do a bunch of things, in order. Get db credentials out of a file,\n read the data from the db, write that data to another file.\n- If anything fails, do not continue.\n- I still have to provide an array of functions, which is a lot of boilerplate,\n and a pita if your functions take args like\n\n```javascript\nfunction (cb) {\n blah(a, b, c, cb)\n}\n```\n\n- Results are discarded, which is a bit lame.\n- No way to branch.\n\n## Solution\n\n- reduces boilerplate by converting an array of [fn, args] to an actor\n that takes no arguments (except cb)\n- A bit like Function#bind, but tailored for our use-case.\n- bindActor(obj, \"method\", a, b, c)\n- bindActor(fn, a, b, c)\n- bindActor(obj, fn, a, b, c)\n- branching, skipping over falsey arguments\n\n```javascript\nchain([\n doThing && [thing, a, b, c]\n, isFoo && [doFoo, \"foo\"]\n, subChain && [chain, [one, two]]\n], cb)\n```\n\n- tracking results: results are stored in an optional array passed as argument,\n last result is always in results[results.length - 1].\n- treat chain.first and chain.last as placeholders for the first/last\n result up until that point.\n\n\n## Non-trivial example\n\n- Read number files in a directory\n- Add the results together\n- Ping a web service with the result\n- Write the response to a file\n- Delete the number files\n\n```javascript\nvar chain = require(\"slide\").chain\nfunction myProgram (cb) {\n var res = [], last = chain.last, first = chain.first\n chain([\n [fs, \"readdir\", \"the-directory\"]\n , [readFiles, \"the-directory\", last]\n , [sum, last]\n , [ping, \"POST\", \"example.com\", 80, \"/foo\", last]\n , [fs, \"writeFile\", \"result.txt\", last]\n , [rmFiles, \"./the-directory\", first]\n ], res, cb)\n}\n```\n\n# Conclusion: Convention Profits\n\n- Consistent API from top to bottom.\n- Sneak in at any point to inject functionality. Testable, reusable, ...\n- When ruby and python users whine, you can smile condescendingly.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/isaacs/slide-flow-control/issues"
+ },
+ "_id": "slide@1.1.4",
+ "_from": "slide@latest"
}
{
- "version": "1.2.18",
+ "version": "1.2.20",
"name": "npm",
"publishConfig": {
"proprietary-attribs": false
"dependencies": {
"semver": "~1.1.2",
"ini": "~1.1.0",
- "slide": "1",
+ "slide": "~1.1.4",
"abbrev": "~1.0.4",
"graceful-fs": "~1.2.0",
- "minimatch": "~0.2.11",
+ "minimatch": "~0.2.12",
"nopt": "~2.1.1",
"rimraf": "2",
- "request": "~2.16.6",
+ "request": "~2.21.0",
"which": "1",
"tar": "~0.1.17",
"fstream": "~0.1.22",
"mkdirp": "~0.3.3",
"read": "~1.0.4",
"lru-cache": "~2.3.0",
- "node-gyp": "~0.9.3",
+ "node-gyp": "~0.9.5",
"fstream-npm": "~0.1.3",
"uid-number": "0",
"archy": "0",
"chownr": "0",
"npmlog": "0",
"ansi": "~0.1.2",
- "npm-registry-client": "~0.2.20",
- "read-package-json": "~0.3.0",
+ "npm-registry-client": "~0.2.22",
+ "read-package-json": "~0.4.1",
"read-installed": "0",
- "glob": "~3.1.21",
- "init-package-json": "0.0.6",
+ "glob": "~3.2.1",
+ "init-package-json": "0.0.8",
"osenv": "0",
- "lockfile": "~0.3.0",
+ "lockfile": "~0.3.2",
"retry": "~0.6.0",
"once": "~1.1.1",
"npmconf": "0",
"opener": "~1.3.0",
"chmodr": "~0.1.0",
- "cmd-shim": "~1.1.0"
+ "cmd-shim": "~1.1.0",
+ "sha": "~1.0.1",
+ "editor": "0.0.4",
+ "child-process-close": "~0.1.1",
+ "npm-user-validate": "0"
},
"bundleDependencies": [
"semver",
"npmconf",
"opener",
"chmodr",
- "cmd-shim"
+ "cmd-shim",
+ "sha",
+ "child-process-close",
+ "editor",
+ "npm-user-validate"
],
"devDependencies": {
"ronn": "~0.3.6",
var npm = require.resolve('../../bin/npm-cli.js')
var node = process.execPath
-console.error('pkg=%j', pkg)
-
test(function (t) {
var child
require('http').createServer(function (req, res) {
//
// there are plenty of other tests to verify that publish
// itself functions normally.
+ //
+ // Make sure that we don't sit around waiting for lock files
child = spawn(node, [npm, 'publish'], {
- cwd: pkg
+ cwd: pkg,
+ env: {
+ npm_config_cache_lock_stale: 1000,
+ npm_config_cache_lock_wait: 1000,
+ HOME: process.env.HOME,
+ Path: process.env.PATH,
+ PATH: process.env.PATH
+ }
})
})
})