send

Better streaming static file server with Range and conditional-GET support

# send

  Send is Connect's `static()` extracted for generalized use, a streaming static file
  server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework.

## Installation

    $ npm install send

## Examples

  Small:

```js
var http = require('http');
var send = require('send');

var app = http.createServer(function(req, res){
  send(req, req.url).pipe(res);
}).listen(3000);
```

  Serving from a root directory with custom error-handling:

```js
var http = require('http');
var send = require('send');
var url = require('url');

var app = http.createServer(function(req, res){
  // your custom error-handling logic:
  function error(err) {
    res.statusCode = err.status || 500;
    res.end(err.message);
  }

  // your custom directory handling logic:
  function redirect() {
    res.statusCode = 301;
    res.setHeader('Location', req.url + '/');
    res.end('Redirecting to ' + req.url + '/');
  }

  // transfer arbitrary files from within
  // /www/example.com/public/*
  send(req, url.parse(req.url).pathname)
  .root('/www/example.com/public')
  .on('error', error)
  .on('directory', redirect)
  .pipe(res);
}).listen(3000);
```

## API

### Events

  - `error` an error occurred `(err)`
  - `directory` a directory was requested
  - `file` a file was requested `(path, stat)`
  - `stream` file streaming has started `(stream)`
  - `end` streaming has completed

### .root(dir)

  Serve files relative to `path`. Aliased as `.from(dir)`.

### .index(path)

  By default send supports "index.html" files, to disable this
  invoke `.index(false)` or to supply a new index pass a string.

### .maxage(ms)

  Provide a max-age in milliseconds for http caching, defaults to 0.

### .hidden(bool)

  Enable or disable transfer of hidden files, defaults to false.

## Error-handling

  By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.

## Caching

  It does _not_ perform internal caching, you should use a reverse proxy cache such
  as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;).

## Debugging

 To enable `debug()` instrumentation output export __DEBUG__:

```
$ DEBUG=send node app
```

## Running tests

```
$ npm install
$ make test
```

## License

(The MIT License)

Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


// First construct a list of entries to watch or unwatch

if (watchedRoot.filter(child.name, child.parentPath)) {

if (child.isDirectory || child === watchedRoot.entry) {

keep keep replace replace keep keep keep keep

/*
 * Copyright (c) 2013 Adobe Systems Incorporated. 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.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, node: true, nomen: true,
indent: 4, maxerr: 50 */

"use strict";

var fs = require("fs-extra");

/**
 * Initialize the "testing" domain.
 * The testing domain provides utilities for tests.
 */
function init(domainManager) {
    if (!domainManager.hasDomain("testing")) {
        domainManager.registerDomain("testing", {major: 0, minor: 1});
    }
    domainManager.registerCommand(
        "testing",
        "remove",
        fs.remove,
        true,
        "Remove the directory at the path",
        [{
            name: "path",
            type: "string",
            description: "path to the directory to remove"
        }]
    );
    domainManager.registerCommand(
        "testing",
        "copy",
        fs.copy,
        true,
        "Copy a file or directory. The directory can have contents. Like cp -r.",
        [
            {
                name: "src",
                type: "string",
                description: "directory source to copy"
            },
            {
                name: "dest",
                type: "string",
                description: "destination directory"
            }
        ]
    );
}

exports.init = init;


# 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>.


keep keep keep replace keep keep keep keep keep

/*
 * Copyright (c) 2013 Adobe Systems Incorporated. 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.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $ */

/**
 * FileSystem is a model object representing a complete file system. This object creates
 * and manages File and Directory instances, dispatches events when the file system changes,
 * and provides methods for showing 'open' and 'save' dialogs.
 *
 * The FileSystem must be initialized very early during application startup.
 *
 * There are three ways to get File or Directory instances:
 *    * Use FileSystem.resolve() to convert a path to a File/Directory object. This will only
 *      succeed if the file/directory already exists.
 *    * Use FileSystem.getFileForPath()/FileSystem.getDirectoryForPath() if you know the
 *      file/directory already exists, or if you want to create a new entry.
 *    * Use Directory.getContents() to return all entries for the specified Directory.
 *
 * FileSystem dispatches the following events:
 *    change - Sent whenever there is a change in the file system. The handler
 *          is passed up to three arguments: the changed entry and, if that changed entry
 *          is a Directory, a list of entries added to the directory and a list of entries
 *          removed from the Directory. The entry argument can be:
 *          *  a File - the contents of the file have changed, and should be reloaded.
 *          *  a Directory - an immediate child of the directory has been added, removed,
 *             or renamed/moved. Not triggered for "grandchildren".
 *          *  null - a 'wholesale' change happened, and you should assume everything may
 *             have changed.
 *          For changes made externally, there may be a significant delay before a "change" event
 *          is dispatched.
 *    rename - Sent whenever a File or Directory is renamed. All affected File and Directory
 *          objects have been updated to reflect the new path by the time this event is dispatched.
 *          This event should be used to trigger any UI updates that may need to occur when a path
 *          has changed. Note that these events will only be sent for rename operations that happen
 *          within the filesystem. If a file is renamed externally, a change event on the parent
 *          directory will be sent instead.
 *
 * FileSystem may perform caching. But it guarantees:
 *    * File contents & metadata - reads are guaranteed to be up to date (cached data is not used
 *      without first veryifying it is up to date).
 *    * Directory structure / file listing - reads may return cached data immediately, which may not
 *      reflect external changes made recently. (However, changes made via FileSystem itself are always
 *      reflected immediately, as soon as the change operation's callback signals success).
 *
 * The FileSystem doesn't directly read or write contents--this work is done by a low-level
 * implementation object. This allows client code to use the FileSystem API without having to
 * worry about the underlying storage, which could be a local filesystem or a remote server.
 */
define(function (require, exports, module) {
    "use strict";

    var Directory       = require("filesystem/Directory"),
        File            = require("filesystem/File"),
        FileIndex       = require("filesystem/FileIndex"),
        FileSystemError = require("filesystem/FileSystemError"),
        WatchedRoot     = require("filesystem/WatchedRoot");

    /**
     * @constructor
     * The FileSystem is not usable until init() signals its callback.
     */
    function FileSystem() {
        // Create a file index
        this._index = new FileIndex();

        // Initialize the set of watched roots
        this._watchedRoots = {};

        // Initialize the watch/unwatch request queue
        this._watchRequests = [];

        // Initialize the queue of pending external changes
        this._externalChanges = [];
    }

    /**
     * The low-level file system implementation used by this object.
     * This is set in the init() function and cannot be changed.
     */
    FileSystem.prototype._impl = null;

    /**
     * The FileIndex used by this object. This is initialized in the constructor.
     */
    FileSystem.prototype._index = null;

    /**
     * Refcount of any pending filesystem mutation operations (e.g., writes,
     * unlinks, etc.). Used to ensure that external change events aren't processed
     * until after index fixups, operation-specific callbacks, and internal change
     * events are complete. (This is important for distinguishing rename from
     * an unrelated delete-add pair).
     * @type {number}
     */
    FileSystem.prototype._activeChangeCount = 0;

    // For unit testing only
    FileSystem.prototype._getActiveChangeCount = function () {
        return this._activeChangeCount;
    };

    /**
     * Queue of arguments with which to invoke _handleExternalChanges(); triggered
     * once _activeChangeCount drops to zero.
     * @type {!Array.<{path:?string, stat:FileSystemStats=}>}
     */
    FileSystem.prototype._externalChanges = null;

    /** Process all queued watcher results, by calling _handleExternalChange() on each */
    FileSystem.prototype._triggerExternalChangesNow = function () {
        this._externalChanges.forEach(function (info) {
            this._handleExternalChange(info.path, info.stat);
        }, this);
        this._externalChanges.length = 0;
    };

    /**
     * Receives a result from the impl's watcher callback, and either processes it
     * immediately (if _activeChangeCount is 0) or otherwise stores it for later
     * processing.
     * @param {?string} path The fullPath of the changed entry
     * @param {FileSystemStats=} stat An optional stat object for the changed entry
     */
    FileSystem.prototype._enqueueExternalChange = function (path, stat) {
        this._externalChanges.push({path: path, stat: stat});
        if (!this._activeChangeCount) {
            this._triggerExternalChangesNow();
        }
    };

    /**
     * The queue of pending watch/unwatch requests.
     * @type {Array.<{fn: function(), cb: function()}>}
     */
    FileSystem.prototype._watchRequests = null;

    /**
     * Dequeue and process all pending watch/unwatch requests
     */
    FileSystem.prototype._dequeueWatchRequest = function () {
        if (this._watchRequests.length > 0) {
            var request = this._watchRequests[0];

            request.fn.call(null, function () {
                // Apply the given callback
                var callbackArgs = arguments;
                try {
                    request.cb.apply(null, callbackArgs);
                } finally {
                    // Process the remaining watch/unwatch requests
                    this._watchRequests.shift();
                    this._dequeueWatchRequest();
                }
            }.bind(this));
        }
    };

    /**
     * Enqueue a new watch/unwatch request.
     *
     * @param {function()} fn - The watch/unwatch request function.
     * @param {callback()} cb - The callback for the provided watch/unwatch
     *      request function.
     */
    FileSystem.prototype._enqueueWatchRequest = function (fn, cb) {
        // Enqueue the given watch/unwatch request
        this._watchRequests.push({fn: fn, cb: cb});

        // Begin processing the queue if it is not already being processed
        if (this._watchRequests.length === 1) {
            this._dequeueWatchRequest();
        }
    };

    /**
     * The set of watched roots, encoded as a mapping from full paths to WatchedRoot
     * objects which contain a file entry, filter function, and an indication of
     * whether the watched root is inactive, starting up or fully active.
     *
     * @type{Object.<string, WatchedRoot>}
     */
    FileSystem.prototype._watchedRoots = null;

    /**
     * Finds a parent watched root for a given path, or returns null if a parent
     * watched root does not exist.
     *
     * @param{string} fullPath The child path for which a parent watched root is to be found
     * @return{?{entry: FileSystemEntry, filter: function(string) boolean}} The parent
     *      watched root, if it exists, or null.
     */
    FileSystem.prototype._findWatchedRootForPath = function (fullPath) {
        var watchedRoot = null;

        Object.keys(this._watchedRoots).some(function (watchedPath) {
            if (fullPath.indexOf(watchedPath) === 0) {
                watchedRoot = this._watchedRoots[watchedPath];
                return true;
            }
        }, this);

        return watchedRoot;
    };

    /**
     * Helper function to watch or unwatch a filesystem entry beneath a given
     * watchedRoot.
     *
     * @private
     * @param {FileSystemEntry} entry - The FileSystemEntry to watch. Must be a
     *      non-strict descendent of watchedRoot.entry.
     * @param {WatchedRoot} watchedRoot - See FileSystem._watchedRoots.
     * @param {function(?string)} callback - A function that is called once the
     *      watch is complete, possibly with a FileSystemError string.
     * @param {boolean} shouldWatch - Whether the entry should be watched (true)
     *      or unwatched (false).
     */
    FileSystem.prototype._watchOrUnwatchEntry = function (entry, watchedRoot, callback, shouldWatch) {
        var impl = this._impl,
            recursiveWatch = impl.recursiveWatch,
            commandName = shouldWatch ? "watchPath" : "unwatchPath";

        if (recursiveWatch) {
            if (entry !== watchedRoot.entry) {
                // Watch and unwatch calls to children of the watched root are
                // no-ops if the impl supports recursiveWatch
                callback(null);
            } else {
                // The impl will handle finding all subdirectories to watch.
                this._enqueueWatchRequest(function (requestCb) {
                    impl[commandName].call(impl, entry.fullPath, requestCb);
                }.bind(this), callback);
            }
        } else {
            // The impl can't handle recursive watch requests, so it's up to the
            // filesystem to recursively watch or unwatch all subdirectories.
            this._enqueueWatchRequest(function (requestCb) {
                // First construct a list of entries to watch or unwatch
                var entriesToWatchOrUnwatch = [],
                    watchOrUnwatch = impl[commandName].bind(impl);

                var visitor = function (child) {
                    if (watchedRoot.filter(child.name, child.parentPath)) {
                        if (child.isDirectory || child === watchedRoot.entry) {
                            entriesToWatchOrUnwatch.push(child);
                        }
                        return true;
                    }
                    return false;
                };

                entry.visit(visitor, function (err) {
                    if (err) {
                        // Unwatching a file/folder that doesn't exist will
                        // trigger an expected error
                        if (!shouldWatch && (err === FileSystemError.NOT_FOUND) &&
                                (entriesToWatchOrUnwatch.length === 0)) {
                            impl.unwatchPathsWithPrefix(entry.fullPath, requestCb);
                            return;
                        } else {
                            // Unexpected error
                            requestCb(err);
                            return;
                        }
                    }

                    // Then watch or unwatched all these entries
                    var count = entriesToWatchOrUnwatch.length;
                    if (count === 0) {
                        requestCb(null);
                        return;
                    }

                    var watchOrUnwatchCallback = function () {
                        if (--count === 0) {
                            requestCb(null);
                        }
                    };

                    entriesToWatchOrUnwatch.forEach(function (entry) {
                        watchOrUnwatch(entry.fullPath, watchOrUnwatchCallback);
                    });
                });
            }, callback);
        }
    };

    /**
     * Watch a filesystem entry beneath a given watchedRoot.
     *
     * @private
     * @param {FileSystemEntry} entry - The FileSystemEntry to watch. Must be a
     *      non-strict descendent of watchedRoot.entry.
     * @param {WatchedRoot} watchedRoot - See FileSystem._watchedRoots.
     * @param {function(?string)} callback - A function that is called once the
     *      watch is complete, possibly with a FileSystemError string.
     */
    FileSystem.prototype._watchEntry = function (entry, watchedRoot, callback) {
        this._watchOrUnwatchEntry(entry, watchedRoot, callback, true);
    };

    /**
     * Unwatch a filesystem entry beneath a given watchedRoot.
     *
     * @private
     * @param {FileSystemEntry} entry - The FileSystemEntry to watch. Must be a
     *      non-strict descendent of watchedRoot.entry.
     * @param {WatchedRoot} watchedRoot - See FileSystem._watchedRoots.
     * @param {function(?string)} callback - A function that is called once the
     *      watch is complete, possibly with a FileSystemError string.
     */
    FileSystem.prototype._unwatchEntry = function (entry, watchedRoot, callback) {
        this._watchOrUnwatchEntry(entry, watchedRoot, function (err) {
            // Make sure to clear cached data for all unwatched entries because
            // entries always return cached data if it exists!
            this._index.visitAll(function (child) {
                if (child.fullPath.indexOf(entry.fullPath) === 0) {
                    child._clearCachedData();
                }
            }.bind(this));

            callback(err);
        }.bind(this), false);
    };

    /**
     * Initialize this FileSystem instance.
     *
     * @param {FileSystemImpl} impl The back-end implementation for this
     *      FileSystem instance.
     */
    FileSystem.prototype.init = function (impl) {
        console.assert(!this._impl, "This FileSystem has already been initialized!");

        var changeCallback = this._enqueueExternalChange.bind(this),
            offlineCallback = this._unwatchAll.bind(this);

        this._impl = impl;
        this._impl.initWatchers(changeCallback, offlineCallback);
    };

    /**
     * Close a file system. Clear all caches, indexes, and file watchers.
     */
    FileSystem.prototype.close = function () {
        this._impl.unwatchAll();
        this._index.clear();
    };

    /**
     * Returns true if the given path should be automatically added to the index & watch list when one of its ancestors
     * is a watch-root. (Files are added automatically when the watch-root is first established, or later when a new
     * directory is created and its children enumerated).
     *
     * Entries explicitly created via FileSystem.getFile/DirectoryForPath() are *always* added to the index regardless
     * of this filtering - but they will not be watched if the watch-root's filter excludes them.
     *
     * @param {string} path Full path
     * @param {string} name Name portion of the path
     */
    FileSystem.prototype._indexFilter = function (path, name) {
        var parentRoot = this._findWatchedRootForPath(path);

        if (parentRoot) {
            return parentRoot.filter(name, path);
        }

        // It might seem more sensible to return false (exclude) for files outside the watch roots, but
        // that would break usage of appFileSystem for 'system'-level things like enumerating extensions.
        // (Or in general, Directory.getContents() for any Directory outside the watch roots).
        return true;
    };

    /**
     * Indicates that a filesystem-mutating operation has begun. As long as there
     * are changes taking place, change events from the external watchers are
     * blocked and queued, to be handled once changes have finished. This is done
     * because for mutating operations that originate from within the filesystem,
     * synthetic change events are fired that do not depend on external file
     * watchers, and we prefer the former over the latter for the following
     * reasons: 1) there is no delay; and 2) they may have higher fidelity ---
     * e.g., a rename operation can be detected as such, instead of as a nearly
     * simultaneous addition and deletion.
     *
     * All operations that mutate the file system MUST begin with a call to
     * _beginChange and must end with a call to _endChange.
     */
    FileSystem.prototype._beginChange = function () {
        this._activeChangeCount++;
        //console.log("> beginChange  -> " + this._activeChangeCount);
    };

    /**
     * Indicates that a filesystem-mutating operation has completed. See
     * FileSystem._beginChange above.
     */
    FileSystem.prototype._endChange = function () {
        this._activeChangeCount--;
        //console.log("< endChange    -> " + this._activeChangeCount);

        if (this._activeChangeCount < 0) {
            console.error("FileSystem _activeChangeCount has fallen below zero!");
        }

        if (!this._activeChangeCount) {
            this._triggerExternalChangesNow();
        }
    };

    /**
     * Determines whether or not the supplied path is absolute, as opposed to relative.
     *
     * @param {!string} fullPath
     * @return {boolean} True if the fullPath is absolute and false otherwise.
     */
    FileSystem.isAbsolutePath = function (fullPath) {
        return (fullPath[0] === "/" || fullPath[1] === ":");
    };

    function _ensureTrailingSlash(path) {
        if (path[path.length - 1] !== "/") {
            path += "/";
        }

        return path;
    }

    /*
     * Matches continguous groups of forward slashes
     * @const
     */
    var _DUPLICATED_SLASH_RE = /\/{2,}/g;

    /**
     * Returns a canonical version of the path: no duplicated "/"es, no ".."s,
     * and directories guaranteed to end in a trailing "/"
     * @param {!string} path  Absolute path, using "/" as path separator
     * @param {boolean=} isDirectory
     * @return {!string}
     */
    FileSystem.prototype._normalizePath = function (path, isDirectory) {

        if (!FileSystem.isAbsolutePath(path)) {
            throw new Error("Paths must be absolute: '" + path + "'");  // expect only absolute paths
        }

        var isUNCPath = this._impl.normalizeUNCPaths && path.search(_DUPLICATED_SLASH_RE) === 0;

        // Remove duplicated "/"es
        path = path.replace(_DUPLICATED_SLASH_RE, "/");

        // Remove ".." segments
        if (path.indexOf("..") !== -1) {
            var segments = path.split("/"),
                i;
            for (i = 1; i < segments.length; i++) {
                if (segments[i] === "..") {
                    if (i < 2) {
                        throw new Error("Invalid absolute path: '" + path + "'");
                    }
                    segments.splice(i - 1, 2);
                    i -= 2; // compensate so we start on the right index next iteration
                }
            }
            path = segments.join("/");
        }

        if (isDirectory) {
            // Make sure path DOES include trailing slash
            path = _ensureTrailingSlash(path);
        }

        if (isUNCPath) {
            // Restore the leading double slash that was removed previously
            path = "/" + path;
        }

        return path;
    };

    /**
     * Return a (strict subclass of a) FileSystemEntry object for the specified
     * path using the provided constuctor. For now, the provided constructor
     * should be either File or Directory.
     *
     * @private
     * @param {function(string, FileSystem)} EntryConstructor Constructor with
     *      which to initialize new FileSystemEntry objects.
     * @param {string} path Absolute path of file.
     * @return {File|Directory} The File or Directory object. This file may not
     *      yet exist on disk.
     */
    FileSystem.prototype._getEntryForPath = function (EntryConstructor, path) {
        var isDirectory = EntryConstructor === Directory;
        path = this._normalizePath(path, isDirectory);
        var entry = this._index.getEntry(path);

        if (!entry) {
            entry = new EntryConstructor(path, this);
            this._index.addEntry(entry);
        }

        return entry;
    };

    /**
     * Return a File object for the specified path.
     *
     * @param {string} path Absolute path of file.
     *
     * @return {File} The File object. This file may not yet exist on disk.
     */
    FileSystem.prototype.getFileForPath = function (path) {
        return this._getEntryForPath(File, path);
    };

    /**
     * Return a Directory object for the specified path.
     *
     * @param {string} path Absolute path of directory.
     *
     * @return {Directory} The Directory object. This directory may not yet exist on disk.
     */
    FileSystem.prototype.getDirectoryForPath = function (path) {
        return this._getEntryForPath(Directory, path);
    };

    /**
     * Resolve a path.
     *
     * @param {string} path The path to resolve
     * @param {function (?string, FileSystemEntry=, FileSystemStats=)} callback Callback resolved
     *      with a FileSystemError string or with the entry for the provided path.
     */
    FileSystem.prototype.resolve = function (path, callback) {
        var normalizedPath = this._normalizePath(path, false),
            item = this._index.getEntry(normalizedPath);

        if (!item) {
            normalizedPath = _ensureTrailingSlash(normalizedPath);
            item = this._index.getEntry(normalizedPath);
        }

        if (item) {
            item.stat(function (err, stat) {
                if (err) {
                    callback(err);
                    return;
                }

                callback(null, item, stat);
            });
        } else {
            this._impl.stat(path, function (err, stat) {
                if (err) {
                    callback(err);
                    return;
                }

                if (stat.isFile) {
                    item = this.getFileForPath(path);
                } else {
                    item = this.getDirectoryForPath(path);
                }

                if (item._isWatched()) {
                    item._stat = stat;
                }

                callback(null, item, stat);
            }.bind(this));
        }
    };

    /**
     * Show an "Open" dialog and return the file(s)/directories selected by the user.
     *
     * @param {boolean} allowMultipleSelection Allows selecting more than one file at a time
     * @param {boolean} chooseDirectories Allows directories to be opened
     * @param {string} title The title of the dialog
     * @param {string} initialPath The folder opened inside the window initially. If initialPath
     *                          is not set, or it doesn't exist, the window would show the last
     *                          browsed folder depending on the OS preferences
     * @param {?Array.<string>} fileTypes (Currently *ignored* except on Mac - https://trello.com/c/430aXkpq)
     *                          List of extensions that are allowed to be opened, without leading ".".
     *                          Null or empty array allows all files to be selected. Not applicable
     *                          when chooseDirectories = true.
     * @param {function (?string, Array.<string>=)} callback Callback resolved with a FileSystemError
     *                          string or the selected file(s)/directories. If the user cancels the
     *                          open dialog, the error will be falsy and the file/directory array will
     *                          be empty.
     */
    FileSystem.prototype.showOpenDialog = function (allowMultipleSelection,
                            chooseDirectories,
                            title,
                            initialPath,
                            fileTypes,
                            callback) {

        this._impl.showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback);
    };

    /**
     * Show a "Save" dialog and return the path of the file to save.
     *
     * @param {string} title The title of the dialog.
     * @param {string} initialPath The folder opened inside the window initially. If initialPath
     *                          is not set, or it doesn't exist, the window would show the last
     *                          browsed folder depending on the OS preferences.
     * @param {string} proposedNewFilename Provide a new file name for the user. This could be based on
     *                          on the current file name plus an additional suffix
     * @param {function (?string, string=)} callback Callback that is resolved with a FileSystemError
     *                          string or the name of the file to save. If the user cancels the save,
     *                          the error will be falsy and the name will be empty.
     */
    FileSystem.prototype.showSaveDialog = function (title, initialPath, proposedNewFilename, callback) {
        this._impl.showSaveDialog(title, initialPath, proposedNewFilename, callback);
    };

    /**
     * Fire a rename event. Clients listen for these events using FileSystem.on.
     *
     * @param {string} oldPath The entry's previous fullPath
     * @param {string} newPath The entry's current fullPath
     */
    FileSystem.prototype._fireRenameEvent = function (oldPath, newPath) {
        $(this).trigger("rename", [oldPath, newPath]);
    };

    /**
     * Fire a change event. Clients listen for these events using FileSystem.on.
     *
     * @param {File|Directory} entry The entry that has changed
     * @param {Array<File|Directory>=} added If the entry is a directory, this
     *      is a set of new entries in the directory.
     * @param {Array<File|Directory>=} removed If the entry is a directory, this
     *      is a set of removed entries from the directory.
     */
    FileSystem.prototype._fireChangeEvent = function (entry, added, removed) {
        $(this).trigger("change", [entry, added, removed]);
    };

    /**
     * @private
     * Notify the system when an entry name has changed.
     *
     * @param {string} oldFullPath
     * @param {string} newFullPath
     * @param {boolean} isDirectory
     */
    FileSystem.prototype._handleRename = function (oldFullPath, newFullPath, isDirectory) {
        // Update all affected entries in the index
        this._index.entryRenamed(oldFullPath, newFullPath, isDirectory);
    };

    /**
     * Notify the filesystem that the given directory has changed. Updates the filesystem's
     * internal state as a result of the change, and calls back with the set of added and
     * removed entries. Mutating FileSystemEntry operations should call this method before
     * applying the operation's callback, and pass along the resulting change sets in the
     * internal change event.
     *
     * @param {Directory} directory The directory that has changed.
     * @param {function(Array<File|Directory>=, Array<File|Directory>=)} callback
     *      The callback that will be applied to a set of added and a set of removed
     *      FileSystemEntry objects.
     */
    FileSystem.prototype._handleDirectoryChange = function (directory, callback) {
        var oldContents = directory._contents;

        directory._clearCachedData();
        directory.getContents(function (err, contents) {
            var addedEntries = oldContents && contents.filter(function (entry) {
                return oldContents.indexOf(entry) === -1;
            });

            var removedEntries = oldContents && oldContents.filter(function (entry) {
                return contents.indexOf(entry) === -1;
            });

            // If directory is not watched, clear children's caches manually.
            var watchedRoot = this._findWatchedRootForPath(directory.fullPath);
            if (!watchedRoot || !watchedRoot.filter(directory.name, directory.parentPath)) {
                this._index.visitAll(function (entry) {
                    if (entry.fullPath.indexOf(directory.fullPath) === 0) {
                        entry._clearCachedData();
                    }
                }.bind(this));

                callback(addedEntries, removedEntries);
                return;
            }

            var addedCounter = addedEntries ? addedEntries.length : 0,
                removedCounter = removedEntries ? removedEntries.length : 0,
                counter = addedCounter + removedCounter;

            if (counter === 0) {
                callback(addedEntries, removedEntries);
                return;
            }

            var watchOrUnwatchCallback = function (err) {
                if (err) {
                    console.error("FileSystem error in _handleDirectoryChange after watch/unwatch entries: " + err);
                }

                if (--counter === 0) {
                    callback(addedEntries, removedEntries);
                }
            };

            if (addedEntries) {
                addedEntries.forEach(function (entry) {
                    this._watchEntry(entry, watchedRoot, watchOrUnwatchCallback);
                }, this);
            }

            if (removedEntries) {
                removedEntries.forEach(function (entry) {
                    this._unwatchEntry(entry, watchedRoot, watchOrUnwatchCallback);
                }, this);
            }
        }.bind(this));
    };

    /**
     * @private
     * Processes a result from the file/directory watchers. Watch results are sent from the low-level implementation
     * whenever a directory or file is changed.
     *
     * @param {string} path The path that changed. This could be a file or a directory.
     * @param {FileSystemStats=} stat Optional stat for the item that changed. This param is not always
     *         passed.
     */
    FileSystem.prototype._handleExternalChange = function (path, stat) {

        if (!path) {
            // This is a "wholesale" change event; clear all caches
            this._index.visitAll(function (entry) {
                entry._clearCachedData();
            });

            this._fireChangeEvent(null);
            return;
        }

        path = this._normalizePath(path, false);

        var entry = this._index.getEntry(path);
        if (entry) {
            var oldStat = entry._stat;
            if (entry.isFile) {
                // Update stat and clear contents, but only if out of date
                if (!(stat && oldStat && stat.mtime.getTime() === oldStat.mtime.getTime())) {
                    entry._clearCachedData();
                    entry._stat = stat;
                    this._fireChangeEvent(entry);
                }
            } else {
                this._handleDirectoryChange(entry, function (added, removed) {
                    entry._stat = stat;
                    if (!(added && added.length === 0 && removed && removed.length === 0)) {
                        this._fireChangeEvent(entry, added, removed);
                    }
                }.bind(this));
            }
        }
    };

    /**
     * Start watching a filesystem root entry.
     *
     * @param {FileSystemEntry} entry - The root entry to watch. If entry is a directory,
     *      all subdirectories that aren't explicitly filtered will also be watched.
     * @param {function(string): boolean} filter - Returns true if a particular item should
     *      be watched, given its name (not full path). Items that are ignored are also
     *      filtered from Directory.getContents() results within this subtree.
     * @param {function(?string)=} callback - A function that is called when the watch has
     *      completed. If the watch fails, the function will have a non-null FileSystemError
     *      string parametr.
     */
    FileSystem.prototype.watch = function (entry, filter, callback) {
        var fullPath = entry.fullPath;

        callback = callback || function () {};

        var watchingParentRoot = this._findWatchedRootForPath(fullPath);
        if (watchingParentRoot &&
                (watchingParentRoot.status === WatchedRoot.STARTING ||
                 watchingParentRoot.status === WatchedRoot.ACTIVE)) {
            callback("A parent of this root is already watched");
            return;
        }

        var watchingChildRoot = Object.keys(this._watchedRoots).some(function (path) {
            var watchedRoot = this._watchedRoots[path],
                watchedPath = watchedRoot.entry.fullPath;

            return watchedPath.indexOf(fullPath) === 0;
        }, this);

        if (watchingChildRoot &&
                (watchingChildRoot.status === WatchedRoot.STARTING ||
                 watchingChildRoot.status === WatchedRoot.ACTIVE)) {
            callback("A child of this root is already watched");
            return;
        }

        var watchedRoot = new WatchedRoot(entry, filter);

        this._watchedRoots[fullPath] = watchedRoot;

        // Enter the STARTING state early to indiate that watched Directory
        // objects may cache their contents. See FileSystemEntry._isWatched.
        watchedRoot.status = WatchedRoot.STARTING;

        this._watchEntry(entry, watchedRoot, function (err) {
            if (err) {
                console.warn("Failed to watch root: ", entry.fullPath, err);
                delete this._watchedRoots[fullPath];
                callback(err);
                return;
            }

            watchedRoot.status = WatchedRoot.ACTIVE;

            callback(null);
        }.bind(this));
    };

    /**
     * Stop watching a filesystem root entry.
     *
     * @param {FileSystemEntry} entry - The root entry to stop watching. The unwatch will
     *      if the entry is not currently being watched.
     * @param {function(?string)=} callback - A function that is called when the unwatch has
     *      completed. If the unwatch fails, the function will have a non-null FileSystemError
     *      string parameter.
     */
    FileSystem.prototype.unwatch = function (entry, callback) {
        var fullPath = entry.fullPath,
            watchedRoot = this._watchedRoots[fullPath];

        callback = callback || function () {};

        if (!watchedRoot) {
            callback("Root is not watched.");
            return;
        }

        // Mark this as inactive, but don't delete the entry until the unwatch is complete.
        // This is useful for making sure we don't try to concurrently watch overlapping roots.
        watchedRoot.status = WatchedRoot.INACTIVE;

        this._unwatchEntry(entry, watchedRoot, function (err) {
            delete this._watchedRoots[fullPath];

            this._index.visitAll(function (child) {
                if (child.fullPath.indexOf(entry.fullPath) === 0) {
                    this._index.removeEntry(child);
                }
            }.bind(this));

            if (err) {
                console.warn("Failed to unwatch root: ", entry.fullPath, err);
                callback(err);
                return;
            }

            callback(null);
        }.bind(this));
    };

    /**
     * Unwatch all watched roots. Calls unwatch on the underlying impl for each
     * watched root and ignores errors.
     * @private
     */
    FileSystem.prototype._unwatchAll = function () {
        console.warn("File watchers went offline!");

        Object.keys(this._watchedRoots).forEach(function (path) {
            var watchedRoot = this._watchedRoots[path];

            watchedRoot.status = WatchedRoot.INACTIVE;
            delete this._watchedRoots[path];
            this._unwatchEntry(watchedRoot.entry, watchedRoot, function () {
                console.warn("Watching disabled for", watchedRoot.entry.fullPath);
            });
        }, this);

        // Fire a wholesale change event, clearing all caches and request that
        // clients manually update their state.
        this._handleExternalChange(null);
    };

    // The singleton instance
    var _instance;

    function _wrap(func) {
        return function () {
            return func.apply(_instance, arguments);
        };
    }

    // Export public methods as proxies to the singleton instance
    exports.init = _wrap(FileSystem.prototype.init);
    exports.close = _wrap(FileSystem.prototype.close);
    exports.shouldShow = _wrap(FileSystem.prototype.shouldShow);
    exports.getFileForPath = _wrap(FileSystem.prototype.getFileForPath);
    exports.getDirectoryForPath = _wrap(FileSystem.prototype.getDirectoryForPath);
    exports.resolve = _wrap(FileSystem.prototype.resolve);
    exports.showOpenDialog = _wrap(FileSystem.prototype.showOpenDialog);
    exports.showSaveDialog = _wrap(FileSystem.prototype.showSaveDialog);
    exports.watch = _wrap(FileSystem.prototype.watch);
    exports.unwatch = _wrap(FileSystem.prototype.unwatch);

    // Static public utility methods
    exports.isAbsolutePath = FileSystem.isAbsolutePath;

    // For testing only
    exports._getActiveChangeCount = _wrap(FileSystem.prototype._getActiveChangeCount);

    /**
     * Add an event listener for a FileSystem event.
     *
     * @param {string} event The name of the event
     * @param {function} handler The handler for the event
     */
    exports.on = function (event, handler) {
        $(_instance).on(event, handler);
    };

    /**
     * Remove an event listener for a FileSystem event.
     *
     * @param {string} event The name of the event
     * @param {function} handler The handler for the event
     */
    exports.off = function (event, handler) {
        $(_instance).off(event, handler);
    };

    // Export the FileSystem class as "private" for unit testing only.
    exports._FileSystem = FileSystem;

    // Create the singleton instance
    _instance = new FileSystem();

    // Initialize the singleton instance
    _instance.init(require("fileSystemImpl"));
});


var Transform = require('../transform');
var inherits = require('util').inherits;

// subclass
function MyStream () {
  Transform.call(this, {
    lowWaterMark: 0,
    encoding: 'utf8'
  });
}
inherits(MyStream, Transform);

MyStream.prototype._transform = function (chunk, outputFn, callback) {
  outputFn(new Buffer(String(chunk).toUpperCase()));
  callback();
};

// use it!
var s = new MyStream();
process.stdin.resume();
process.stdin.pipe(s).pipe(process.stdout);
if (process.stdin.setRawMode)
  process.stdin.setRawMode(true);
process.stdin.on('data', function (c) {
  c = c.toString();
  if (c === '\u0003' || c === '\u0004') {
    process.stdin.pause();
    s.end();
  }
  if (c === '\r')
    process.stdout.write('\n');
});


function Player() {
}
Player.prototype.play = function(song) {
  this.currentlyPlayingSong = song;
  this.isPlaying = true;
};

Player.prototype.pause = function() {
  this.isPlaying = false;
};

Player.prototype.resume = function() {
  if (this.isPlaying) {
    throw new Error("song is already playing");
  }

  this.isPlaying = true;
};

Player.prototype.makeFavorite = function() {
  this.currentlyPlayingSong.persistFavoriteStatus(true);
};


var tap = require('tap');
var test = tap.test;

test('no module system', function(t) {
  var fs = require('fs');
  var vm = require('vm');
  var head = fs.readFileSync(require.resolve('../head.js'), 'utf8');
  var src = fs.readFileSync(require.resolve('../'), 'utf8');
  var foot = fs.readFileSync(require.resolve('../foot.js'), 'utf8');
  vm.runInThisContext(head + src + foot, 'semver.js');

  // just some basic poking to see if it did some stuff
  t.type(global.semver, 'object');
  t.type(global.semver.SemVer, 'function');
  t.type(global.semver.Range, 'function');
  t.ok(global.semver.satisfies('1.2.3', '1.2'));
  t.end();
});


// Unwatching a file/folder that doesn't exist will

if (!shouldWatch && (err === FileSystemError.NOT_FOUND) &&

(entriesToWatchOrUnwatch.length === 0)) {

// Then watch or unwatched all these entries

keep keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace keep keep keep


// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var Transform = require('./lib/_stream_transform.js');

var binding = process.binding('zlib');
var util = require('util');
var assert = require('assert').ok;

// zlib doesn't provide these, so kludge them in following the same
// const naming scheme zlib uses.
binding.Z_MIN_WINDOWBITS = 8;
binding.Z_MAX_WINDOWBITS = 15;
binding.Z_DEFAULT_WINDOWBITS = 15;

// fewer than 64 bytes per chunk is stupid.
// technically it could work with as few as 8, but even 64 bytes
// is absurdly low.  Usually a MB or more is best.
binding.Z_MIN_CHUNK = 64;
binding.Z_MAX_CHUNK = Infinity;
binding.Z_DEFAULT_CHUNK = (16 * 1024);

binding.Z_MIN_MEMLEVEL = 1;
binding.Z_MAX_MEMLEVEL = 9;
binding.Z_DEFAULT_MEMLEVEL = 8;

binding.Z_MIN_LEVEL = -1;
binding.Z_MAX_LEVEL = 9;
binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;

// expose all the zlib constants
Object.keys(binding).forEach(function(k) {
  if (k.match(/^Z/)) exports[k] = binding[k];
});

// translation table for return codes.
exports.codes = {
  Z_OK: binding.Z_OK,
  Z_STREAM_END: binding.Z_STREAM_END,
  Z_NEED_DICT: binding.Z_NEED_DICT,
  Z_ERRNO: binding.Z_ERRNO,
  Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
  Z_DATA_ERROR: binding.Z_DATA_ERROR,
  Z_MEM_ERROR: binding.Z_MEM_ERROR,
  Z_BUF_ERROR: binding.Z_BUF_ERROR,
  Z_VERSION_ERROR: binding.Z_VERSION_ERROR
};

Object.keys(exports.codes).forEach(function(k) {
  exports.codes[exports.codes[k]] = k;
});

exports.Deflate = Deflate;
exports.Inflate = Inflate;
exports.Gzip = Gzip;
exports.Gunzip = Gunzip;
exports.DeflateRaw = DeflateRaw;
exports.InflateRaw = InflateRaw;
exports.Unzip = Unzip;

exports.createDeflate = function(o) {
  return new Deflate(o);
};

exports.createInflate = function(o) {
  return new Inflate(o);
};

exports.createDeflateRaw = function(o) {
  return new DeflateRaw(o);
};

exports.createInflateRaw = function(o) {
  return new InflateRaw(o);
};

exports.createGzip = function(o) {
  return new Gzip(o);
};

exports.createGunzip = function(o) {
  return new Gunzip(o);
};

exports.createUnzip = function(o) {
  return new Unzip(o);
};

// Convenience methods.
// compress/decompress a string or buffer in one step.
exports.deflate = function(buffer, callback) {
  zlibBuffer(new Deflate(), buffer, callback);
};

exports.gzip = function(buffer, callback) {
  zlibBuffer(new Gzip(), buffer, callback);
};

exports.deflateRaw = function(buffer, callback) {
  zlibBuffer(new DeflateRaw(), buffer, callback);
};

exports.unzip = function(buffer, callback) {
  zlibBuffer(new Unzip(), buffer, callback);
};

exports.inflate = function(buffer, callback) {
  zlibBuffer(new Inflate(), buffer, callback);
};

exports.gunzip = function(buffer, callback) {
  zlibBuffer(new Gunzip(), buffer, callback);
};

exports.inflateRaw = function(buffer, callback) {
  zlibBuffer(new InflateRaw(), buffer, callback);
};

function zlibBuffer(engine, buffer, callback) {
  var buffers = [];
  var nread = 0;

  engine.on('error', onError);
  engine.on('end', onEnd);

  engine.end(buffer);
  flow();

  function flow() {
    var chunk;
    while (null !== (chunk = engine.read())) {
      buffers.push(chunk);
      nread += chunk.length;
    }
    engine.once('readable', flow);
  }

  function onError(err) {
    engine.removeListener('end', onEnd);
    engine.removeListener('readable', flow);
    callback(err);
  }

  function onEnd() {
    var buf = Buffer.concat(buffers, nread);
    buffers = [];
    callback(null, buf);
  }
}

// generic zlib
// minimal 2-byte header
function Deflate(opts) {
  if (!(this instanceof Deflate)) return new Deflate(opts);
  Zlib.call(this, opts, binding.DEFLATE);
}

function Inflate(opts) {
  if (!(this instanceof Inflate)) return new Inflate(opts);
  Zlib.call(this, opts, binding.INFLATE);
}

// gzip - bigger header, same deflate compression
function Gzip(opts) {
  if (!(this instanceof Gzip)) return new Gzip(opts);
  Zlib.call(this, opts, binding.GZIP);
}

function Gunzip(opts) {
  if (!(this instanceof Gunzip)) return new Gunzip(opts);
  Zlib.call(this, opts, binding.GUNZIP);
}

// raw - no header
function DeflateRaw(opts) {
  if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
  Zlib.call(this, opts, binding.DEFLATERAW);
}

function InflateRaw(opts) {
  if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
  Zlib.call(this, opts, binding.INFLATERAW);
}

// auto-detect header.
function Unzip(opts) {
  if (!(this instanceof Unzip)) return new Unzip(opts);
  Zlib.call(this, opts, binding.UNZIP);
}

// the Zlib class they all inherit from
// This thing manages the queue of requests, and returns
// true or false if there is anything in the queue when
// you call the .write() method.

function Zlib(opts, mode) {
  this._opts = opts = opts || {};
  this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;

  Transform.call(this, opts);

  // means a different thing there.
  this._readableState.chunkSize = null;

  if (opts.chunkSize) {
    if (opts.chunkSize < exports.Z_MIN_CHUNK ||
        opts.chunkSize > exports.Z_MAX_CHUNK) {
      throw new Error('Invalid chunk size: ' + opts.chunkSize);
    }
  }

  if (opts.windowBits) {
    if (opts.windowBits < exports.Z_MIN_WINDOWBITS ||
        opts.windowBits > exports.Z_MAX_WINDOWBITS) {
      throw new Error('Invalid windowBits: ' + opts.windowBits);
    }
  }

  if (opts.level) {
    if (opts.level < exports.Z_MIN_LEVEL ||
        opts.level > exports.Z_MAX_LEVEL) {
      throw new Error('Invalid compression level: ' + opts.level);
    }
  }

  if (opts.memLevel) {
    if (opts.memLevel < exports.Z_MIN_MEMLEVEL ||
        opts.memLevel > exports.Z_MAX_MEMLEVEL) {
      throw new Error('Invalid memLevel: ' + opts.memLevel);
    }
  }

  if (opts.strategy) {
    if (opts.strategy != exports.Z_FILTERED &&
        opts.strategy != exports.Z_HUFFMAN_ONLY &&
        opts.strategy != exports.Z_RLE &&
        opts.strategy != exports.Z_FIXED &&
        opts.strategy != exports.Z_DEFAULT_STRATEGY) {
      throw new Error('Invalid strategy: ' + opts.strategy);
    }
  }

  if (opts.dictionary) {
    if (!Buffer.isBuffer(opts.dictionary)) {
      throw new Error('Invalid dictionary: it should be a Buffer instance');
    }
  }

  this._binding = new binding.Zlib(mode);

  var self = this;
  this._hadError = false;
  this._binding.onerror = function(message, errno) {
    // there is no way to cleanly recover.
    // continuing only obscures problems.
    self._binding = null;
    self._hadError = true;

    var error = new Error(message);
    error.errno = errno;
    error.code = exports.codes[errno];
    self.emit('error', error);
  };

  this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
                     opts.level || exports.Z_DEFAULT_COMPRESSION,
                     opts.memLevel || exports.Z_DEFAULT_MEMLEVEL,
                     opts.strategy || exports.Z_DEFAULT_STRATEGY,
                     opts.dictionary);

  this._buffer = new Buffer(this._chunkSize);
  this._offset = 0;
  this._closed = false;

  this.once('end', this.close);
}

util.inherits(Zlib, Transform);

Zlib.prototype.reset = function reset() {
  return this._binding.reset();
};

Zlib.prototype._flush = function(output, callback) {
  var rs = this._readableState;
  var self = this;
  this._transform(null, output, function(er) {
    if (er)
      return callback(er);

    // now a weird thing happens... it could be that you called flush
    // but everything had already actually been consumed, but it wasn't
    // enough to get over the Readable class's lowWaterMark.
    // In that case, we emit 'readable' now to make sure it's consumed.
    if (rs.length &&
        rs.length < rs.lowWaterMark &&
        !rs.ended &&
        rs.needReadable)
      self.emit('readable');

    callback();
  });
};

Zlib.prototype.flush = function(callback) {
  var ws = this._writableState;
  var ts = this._transformState;

  if (ws.writing) {
    ws.needDrain = true;
    var self = this;
    this.once('drain', function() {
      self._flush(ts.output, callback);
    });
    return;
  }

  this._flush(ts.output, callback || function() {});
};

Zlib.prototype.close = function(callback) {
  if (callback)
    process.nextTick(callback);

  if (this._closed)
    return;

  this._closed = true;

  this._binding.close();

  var self = this;
  process.nextTick(function() {
    self.emit('close');
  });
};

Zlib.prototype._transform = function(chunk, output, cb) {
  var flushFlag;
  var ws = this._writableState;
  var ending = ws.ending || ws.ended;
  var last = ending && (!chunk || ws.length === chunk.length);

  if (chunk !== null && !Buffer.isBuffer(chunk))
    return cb(new Error('invalid input'));

  // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag.
  // If it's explicitly flushing at some other time, then we use
  // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
  // goodness.
  if (last)
    flushFlag = binding.Z_FINISH;
  else if (chunk === null)
    flushFlag = binding.Z_FULL_FLUSH;
  else
    flushFlag = binding.Z_NO_FLUSH;

  var availInBefore = chunk && chunk.length;
  var availOutBefore = this._chunkSize - this._offset;
  var inOff = 0;

  var req = this._binding.write(flushFlag,
                                chunk, // in
                                inOff, // in_off
                                availInBefore, // in_len
                                this._buffer, // out
                                this._offset, //out_off
                                availOutBefore); // out_len

  req.buffer = chunk;
  req.callback = callback;

  var self = this;
  function callback(availInAfter, availOutAfter, buffer) {
    if (self._hadError)
      return;

    var have = availOutBefore - availOutAfter;
    assert(have >= 0, 'have should not go down');

    if (have > 0) {
      var out = self._buffer.slice(self._offset, self._offset + have);
      self._offset += have;
      // serve some output to the consumer.
      output(out);
    }

    // exhausted the output buffer, or used all the input create a new one.
    if (availOutAfter === 0 || self._offset >= self._chunkSize) {
      availOutBefore = self._chunkSize;
      self._offset = 0;
      self._buffer = new Buffer(self._chunkSize);
    }

    if (availOutAfter === 0) {
      // Not actually done.  Need to reprocess.
      // Also, update the availInBefore to the availInAfter value,
      // so that if we have to hit it a third (fourth, etc.) time,
      // it'll have the correct byte counts.
      inOff += (availInBefore - availInAfter);
      availInBefore = availInAfter;

      var newReq = self._binding.write(flushFlag,
                                       chunk,
                                       inOff,
                                       availInBefore,
                                       self._buffer,
                                       self._offset,
                                       self._chunkSize);
      newReq.callback = callback; // this same function
      newReq.buffer = chunk;
      return;
    }

    // finished with the chunk.
    cb();
  }
};

util.inherits(Deflate, Zlib);
util.inherits(Inflate, Zlib);
util.inherits(Gzip, Zlib);
util.inherits(Gunzip, Zlib);
util.inherits(DeflateRaw, Zlib);
util.inherits(InflateRaw, Zlib);
util.inherits(Unzip, Zlib);


/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global */

var app = app || {};

(function () {
	'use strict';

    app.c = 3;
}());


var assert = require('assert');
var Buffers = require('../');

function create (xs, split) {
    var bufs = Buffers();
    var offset = 0;
    split.forEach(function (i) {
        bufs.push(new Buffer(xs.slice(offset, offset + i)));
        offset += i;
    });
    return bufs;
}

exports.slice = function () {
    var xs = [0,1,2,3,4,5,6,7,8,9];
    var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];

    splits.forEach(function (split) {
        var bufs = create(xs, split);
        assert.eql(new Buffer(xs), bufs.slice(),
            '[' + xs.join(',') + ']'
                + ' != ' +
            '[' + [].join.call(bufs.slice(), ',') + ']'
        );

        for (var i = 0; i < xs.length; i++) {
            for (var j = i; j < xs.length; j++) {
                var a = bufs.slice(i,j);
                var b = new Buffer(xs.slice(i,j));

                assert.eql(a, b,
                    '[' + [].join.call(a, ',') + ']'
                        + ' != ' +
                    '[' + [].join.call(b, ',') + ']'
                );
            }
        }
    });
};

exports.splice = function () {
    var xs = [0,1,2,3,4,5,6,7,8,9];
    var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];

    splits.forEach(function (split) {
        for (var i = 0; i < xs.length; i++) {
            for (var j = i; j < xs.length; j++) {
                var bufs = create(xs, split);
                var xs_ = xs.slice();

                var a_ = bufs.splice(i,j);
                var a = [].slice.call(a_.slice());
                var b = xs_.splice(i,j);
                assert.eql(a, b,
                    '[' + a.join(',') + ']'
                        + ' != ' +
                    '[' + b.join(',') + ']'
                );

                assert.eql(bufs.slice(), new Buffer(xs_),
                    '[' + [].join.call(bufs.slice(), ',') + ']'
                        + ' != ' +
                    '[' + [].join.call(xs_, ',') + ']'
                );
            }
        }
    });
};

exports.spliceRep = function () {
    var xs = [0,1,2,3,4,5,6,7,8,9];
    var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
    var reps = [ [], [1], [5,6], [3,1,3,3,7], [9,8,7,6,5,4,3,2,1,2,3,4,5] ];

    splits.forEach(function (split) {
        reps.forEach(function (rep) {
            for (var i = 0; i < xs.length; i++) {
                for (var j = i; j < xs.length; j++) {
                    var bufs = create(xs, split);
                    var xs_ = xs.slice();

                    var a_ = bufs.splice.apply(
                        bufs, [ i, j ].concat(new Buffer(rep))
                    );
                    var a = [].slice.call(a_.slice());
                    var b = xs_.splice.apply(xs_, [ i, j ].concat(rep));

                    assert.eql(a, b,
                        '[' + a.join(',') + ']'
                            + ' != ' +
                        '[' + b.join(',') + ']'
                    );

                    assert.eql(bufs.slice(), new Buffer(xs_),
                        '[' + [].join.call(bufs.slice(), ',') + ']'
                            + ' != ' +
                        '[' + [].join.call(xs_, ',') + ']'
                    );
                }
            }
        });
    });
};

exports.copy = function () {
    var xs = [0,1,2,3,4,5,6,7,8,9];
    var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];

    splits.forEach(function (split) {
        var bufs = create(xs, split);
        var buf = new Buffer(xs);

        for (var i = 0; i < xs.length; i++) {
            for (var j = i; j < xs.length; j++) {
                var t0 = new Buffer(j - i);
                var t1 = new Buffer(j - i);

                assert.eql(
                    bufs.copy(t0, 0, i, j),
                    buf.copy(t1, 0, i, j)
                );

                assert.eql(
                    [].slice.call(t0),
                    [].slice.call(t1)
                );
            }
        }
    });
};

exports.push = function () {
    var bufs = Buffers();
    bufs.push(new Buffer([0]));
    bufs.push(new Buffer([1,2,3]));
    bufs.push(new Buffer([4,5]));
    bufs.push(new Buffer([6,7,8,9]));
    assert.eql(
        [].slice.call(bufs.slice()),
        [0,1,2,3,4,5,6,7,8,9]
    );

    assert.throws(function () {
        bufs.push(new Buffer([11,12]), 'moo');
    });
    assert.eql(bufs.buffers.length, 4);
};

exports.unshift = function () {
    var bufs = Buffers();
    bufs.unshift(new Buffer([6,7,8,9]));
    bufs.unshift(new Buffer([4,5]));
    bufs.unshift(new Buffer([1,2,3]));
    bufs.unshift(new Buffer([0]));
    assert.eql(
        [].slice.call(bufs.slice()),
        [0,1,2,3,4,5,6,7,8,9]
    );
    assert.throws(function () {
        bufs.unshift(new Buffer([-2,-1]), 'moo');
    });
    assert.eql(bufs.buffers.length, 4);
};

exports.get = function () {
    var bufs = Buffers();
    bufs.unshift(new Buffer([6,7,8,9]));
    bufs.unshift(new Buffer([4,5]));
    bufs.unshift(new Buffer([1,2,3]));
    bufs.unshift(new Buffer([0]));
    assert.eql( bufs.get(0), 0 );
    assert.eql( bufs.get(1), 1 );
    assert.eql( bufs.get(2), 2 );
    assert.eql( bufs.get(3), 3 );
    assert.eql( bufs.get(4), 4 );
    assert.eql( bufs.get(5), 5 );
    assert.eql( bufs.get(6), 6 );
    assert.eql( bufs.get(7), 7 );
    assert.eql( bufs.get(8), 8 );
    assert.eql( bufs.get(9), 9 );
};

exports.set = function () {
    var bufs = Buffers();
    bufs.push(new Buffer("Hel"));
    bufs.push(new Buffer("lo"));
    bufs.push(new Buffer("!"));
    bufs.set(0, 'h'.charCodeAt(0) );
    bufs.set(3, 'L'.charCodeAt(0) );
    bufs.set(5, '.'.charCodeAt(0) );
    assert.eql( bufs.slice(0).toString(), 'helLo.' );
};

exports.indexOf = function () {
    var bufs = Buffers();
    bufs.push(new Buffer("Hel"));
    bufs.push(new Buffer("lo,"));
    bufs.push(new Buffer(" how are "));
    bufs.push(new Buffer("you"));
    bufs.push(new Buffer("?"));
    assert.eql( bufs.indexOf("Hello"), 0 );
    assert.eql( bufs.indexOf("Hello", 1), -1 );
    assert.eql( bufs.indexOf("ello"), 1 );
    assert.eql( bufs.indexOf("ello", 1), 1 );
    assert.eql( bufs.indexOf("ello", 2), -1 );
    assert.eql( bufs.indexOf("e"), 1 );
    assert.eql( bufs.indexOf("e", 2), 13 );
    assert.eql( bufs.indexOf(new Buffer([0x65]), 2), 13 );
};


var count = entriesToWatchOrUnwatch.length;

keep keep keep replace keep keep keep keep


<!DOCTYPE html>
<html>
<head>
    <script src="./file1.js"></script>
    <script src="./file2.js"></script>
    <!-- leave follow empty line for later file including -->

<script>
    var txt="";
    nonAsciiTest(); // in quick editor, test jump, function type, code hint list
    myTest();
    callOtherMethods();
</script>
<script>
    function myTest(){
        monthName = "test";
        simple1();
    }
    function simple1(){
    }

</script>

</head>

<body>
    <input type="button" value="View message" onclick="message()">
</body>

</html>


/*
    HTTP Hawk Authentication Scheme
    Copyright (c) 2012-2013, Eran Hammer <eran@hueniverse.com>
    MIT Licensed
*/

// Declare namespace

var hawk = {};

// Export if used as a module

if (typeof module !== "undefined" && module.exports) {
    module.exports = hawk;
}

hawk.client = {

    // Generate an Authorization header for a given request

    /*
        uri: 'http://example.com/resource?a=b'
        method: HTTP verb (e.g. 'GET', 'POST')
        options: {

            // Required

            credentials: {
                id: 'dh37fgj492je',
                key: 'aoijedoaijsdlaksjdl',
                algorithm: 'sha256'                                 // 'sha1', 'sha256'
            },

            // Optional

            ext: 'application-specific',                        // Application specific data sent via the ext attribute
            timestamp: Date.now() / 1000,                       // A pre-calculated timestamp in seconds
            nonce: '2334f34f',                                  // A pre-generated nonce
            localtimeOffsetMsec: 400,                           // Time offset to sync with server time (ignored if timestamp provided)
            payload: '{"some":"payload"}',                      // UTF-8 encoded string for body hash generation (ignored if hash provided)
            contentType: 'application/json',                    // Payload content-type (ignored if hash provided)
            hash: 'U4MKKSmiVxk37JCCrAVIjV=',                    // Pre-calculated payload hash
            app: '24s23423f34dx',                               // Oz application id
            dlg: '234sz34tww3sd'                                // Oz delegated-by application id
        }
    */

    header: function (uri, method, options) {

        var result = {
            field: '',
            artifacts: {}
        };

        // Validate inputs

        if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||
            !method || typeof method !== 'string' ||
            !options || typeof options !== 'object') {

            return result;
        }

        // Application time

        var timestamp = options.timestamp || Math.floor((hawk.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 (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
            return result;
        }

        // Parse URI

        if (typeof uri === 'string') {
            uri = hawk.utils.parseUri(uri);
        }

        // Calculate signature

        var artifacts = {
            ts: timestamp,
            nonce: options.nonce || hawk.utils.randomString(6),
            method: method,
            resource: uri.relative,
            host: uri.hostname,
            port: uri.port,
            hash: options.hash,
            ext: options.ext,
            app: options.app,
            dlg: options.dlg
        };

        result.artifacts = artifacts;

        // Calculate payload hash

        if (!artifacts.hash &&
            options.hasOwnProperty('payload')) {

            artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
        }

        var mac = hawk.crypto.calculateMac('header', credentials, artifacts);

        // Construct header

        var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== '';       // Other falsey values allowed
        var header = 'Hawk id="' + credentials.id +
                     '", ts="' + artifacts.ts +
                     '", nonce="' + artifacts.nonce +
                     (artifacts.hash ? '", hash="' + artifacts.hash : '') +
                     (hasExt ? '", ext="' + hawk.utils.escapeHeaderAttribute(artifacts.ext) : '') +
                     '", mac="' + mac + '"';

        if (artifacts.app) {
            header += ', app="' + artifacts.app +
                      (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
        }

        result.field = header;

        return result;
    },

    // Validate server response

    /*
        request:    object created via 'new XMLHttpRequest()' after response received
        artifacts:  object recieved from header().artifacts
        options: {
            payload:    optional payload received
            required:   specifies if a Server-Authorization header is required. Defaults to 'false'
        }
    */

    authenticate: function (request, credentials, artifacts, options) {

        options = options || {};

        if (request.getResponseHeader('www-authenticate')) {

            // Parse HTTP WWW-Authenticate header

            var attributes = hawk.utils.parseAuthorizationHeader(request.getResponseHeader('www-authenticate'), ['ts', 'tsm', 'error']);
            if (!attributes) {
                return false;
            }

            if (attributes.ts) {
                var tsm = hawk.crypto.calculateTsMac(attributes.ts, credentials);
                if (tsm !== attributes.tsm) {
                    return false;
                }

                hawk.utils.setNtpOffset(attributes.ts - Math.floor(Date.now() / 1000));     // Keep offset at 1 second precision
            }
        }

        // Parse HTTP Server-Authorization header

        if (!request.getResponseHeader('server-authorization') &&
            !options.required) {

            return true;
        }

        var attributes = hawk.utils.parseAuthorizationHeader(request.getResponseHeader('server-authorization'), ['mac', 'ext', 'hash']);
        if (!attributes) {
            return false;
        }

        var modArtifacts = {
            ts: artifacts.ts,
            nonce: artifacts.nonce,
            method: artifacts.method,
            resource: artifacts.resource,
            host: artifacts.host,
            port: artifacts.port,
            hash: attributes.hash,
            ext: attributes.ext,
            app: artifacts.app,
            dlg: artifacts.dlg
        };

        var mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
        if (mac !== attributes.mac) {
            return false;
        }

        if (!options.hasOwnProperty('payload')) {
            return true;
        }

        if (!attributes.hash) {
            return false;
        }

        var calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.getResponseHeader('content-type'));
        return (calculatedHash === attributes.hash);
    },

    message: function (host, port, message, options) {

        // Validate inputs

        if (!host || typeof host !== 'string' ||
            !port || typeof port !== 'number' ||
            message === null || message === undefined || typeof message !== 'string' ||
            !options || typeof options !== 'object') {

            return null;
        }

        // Application time

        var timestamp = options.timestamp || Math.floor((hawk.utils.now() + (options.localtimeOffsetMsec || 0)) / 1000)

        // Validate credentials

        var credentials = options.credentials;
        if (!credentials ||
            !credentials.id ||
            !credentials.key ||
            !credentials.algorithm) {

            // Invalid credential object
            return null;
        }

        if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
            return null;
        }

        // Calculate signature

        var artifacts = {
            ts: timestamp,
            nonce: options.nonce || hawk.utils.randomString(6),
            host: host,
            port: port,
            hash: hawk.crypto.calculatePayloadHash(message, credentials.algorithm)
        };

        // Construct authorization

        var result = {
            id: credentials.id,
            ts: artifacts.ts,
            nonce: artifacts.nonce,
            hash: artifacts.hash,
            mac: hawk.crypto.calculateMac('message', credentials, artifacts)
        };

        return result;
    }
};

hawk.crypto = {

    headerVersion: '1',

    algorithms: ['sha1', 'sha256'],

    calculateMac: function (type, credentials, options) {

        var normalized = hawk.crypto.generateNormalizedString(type, options);

        var hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
        return hmac.toString(CryptoJS.enc.Base64);
    },

    generateNormalizedString: function (type, options) {

        var normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' +
                         options.ts + '\n' +
                         options.nonce + '\n' +
                         (options.method || '').toUpperCase() + '\n' +
                         (options.resource || '') + '\n' +
                         options.host.toLowerCase() + '\n' +
                         options.port + '\n' +
                         (options.hash || '') + '\n';

        if (options.ext) {
            normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');
        }

        normalized += '\n';

        if (options.app) {
            normalized += options.app + '\n' +
                          (options.dlg || '') + '\n';
        }

        return normalized;
    },

    calculatePayloadHash: function (payload, algorithm, contentType) {

        var hash = CryptoJS.algo[algorithm.toUpperCase()].create();
        hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');
        hash.update(hawk.utils.parseContentType(contentType) + '\n');
        hash.update(payload || '');
        hash.update('\n');
        return hash.finalize().toString(CryptoJS.enc.Base64);
    },

    calculateTsMac: function (ts, credentials) {

        var hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
        return hash.toString(CryptoJS.enc.Base64);
    }
};

hawk.utils = {

    storage: {                                      // localStorage compatible interface
        _cache: {},
        setItem: function (key, value) {

            hawk.utils.storage._cache[key] = value;
        },
        getItem: function (key) {

            return hawk.utils.storage._cache[key];
        }
    },

    setStorage: function (storage) {

        var ntpOffset = hawk.utils.getNtpOffset() || 0;
        hawk.utils.storage = storage;
        hawk.utils.setNtpOffset(ntpOffset);
    },

    setNtpOffset: function (offset) {

        hawk.utils.storage.setItem('hawk_ntp_offset', offset);
    },

    getNtpOffset: function () {

        return parseInt(hawk.utils.storage.getItem('hawk_ntp_offset') || '0', 10);
    },

    now: function () {

        return Date.now() + hawk.utils.getNtpOffset();
    },

    escapeHeaderAttribute: function (attribute) {

        return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');
    },

    parseContentType: function (header) {

        if (!header) {
            return '';
        }

        return header.split(';')[0].trim().toLowerCase();
    },

    parseAuthorizationHeader: function (header, keys) {

        if (!header) {
            return null;
        }

        var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/);       // Header: scheme[ something]
        if (!headerParts) {
            return null;
        }

        var scheme = headerParts[1];
        if (scheme.toLowerCase() !== 'hawk') {
            return null;
        }

        var attributesString = headerParts[2];
        if (!attributesString) {
            return null;
        }

        var attributes = {};
        var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {

            // Check valid attribute names

            if (keys.indexOf($1) === -1) {
                return;
            }

            // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9

            if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
                return;
            }

            // Check for duplicates

            if (attributes.hasOwnProperty($1)) {
                return;
            }

            attributes[$1] = $2;
            return '';
        });

        if (verify !== '') {
            return null;
        }

        return attributes;
    },

    randomString: function (size) {

        var randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        var len = randomSource.length;

        var result = [];
        for (var i = 0; i < size; ++i) {
            result[i] = randomSource[Math.floor(Math.random() * len)];
        }

        return result.join('');
    },

    parseUri: function (input) {

        // Based on: parseURI 1.2.2
        // http://blog.stevenlevithan.com/archives/parseuri
        // (c) Steven Levithan <stevenlevithan.com>
        // MIT License

        var keys = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'hostname', 'port', 'resource', 'relative', 'pathname', 'directory', 'file', 'query', 'fragment'];

        var uriRegex = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?)(?:#(.*))?)/;
        var uriByNumber = uriRegex.exec(input);
        var uri = {};

        var i = 15;
        while (i--) {
            uri[keys[i]] = uriByNumber[i] || '';
        }

        if (uri.port === null ||
            uri.port === '') {

            uri.port = (uri.protocol.toLowerCase() === 'http' ? '80' : (uri.protocol.toLowerCase() === 'https' ? '443' : ''));
        }

        return uri;
    }
};

// Based on: Crypto-JS v3.1.2
// Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
// http://code.google.com/p/crypto-js/
// http://code.google.com/p/crypto-js/wiki/License

var CryptoJS=CryptoJS||function(h,r){var k={},l=k.lib={},n=function(){},f=l.Base={extend:function(a){n.prototype=this;var b=new n;a&&b.mixIn(a);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},j=l.WordArray=f.extend({init:function(a,b){a=this.words=a||[];this.sigBytes=b!=r?b:4*a.length},toString:function(a){return(a||s).stringify(this)},concat:function(a){var b=this.words,d=a.words,c=this.sigBytes;a=a.sigBytes;this.clamp();if(c%4)for(var e=0;e<a;e++)b[c+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((c+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)b[c+e>>>2]=d[e>>>2];else b.push.apply(b,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*h.random()|0);return new j.init(b,a)}}),m=k.enc={},s=m.Hex={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,2),16)<<24-4*(c%8);return new j.init(d,b/2)}},p=m.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return new j.init(d,b)}},t=m.Utf8={stringify:function(a){try{return decodeURIComponent(escape(p.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return p.parse(unescape(encodeURIComponent(a)))}},q=l.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new j.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=t.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,f=c/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;c=h.min(4*a,c);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);b.sigBytes-=c}return new j.init(g,c)},clone:function(){var a=f.clone.call(this);a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:f.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new u.HMAC.init(a,d)).finalize(b)}}});var u=k.algo={};return k}(Math);
(function () { var k = CryptoJS, b = k.lib, m = b.WordArray, l = b.Hasher, d = [], b = k.algo.SHA1 = l.extend({ _doReset: function () { this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (n, p) { for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) { if (16 > c) d[c] = n[p + c] | 0; else { var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16]; d[c] = g << 1 | g >>> 31 } g = (e << 5 | e >>> 27) + b + d[c]; g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514); b = j; j = h; h = f << 30 | f >>> 2; f = e; e = g } a[0] = a[0] + e | 0; a[1] = a[1] + f | 0; a[2] = a[2] + h | 0; a[3] = a[3] + j | 0; a[4] = a[4] + b | 0 }, _doFinalize: function () { var b = this._data, d = b.words, a = 8 * this._nDataBytes, e = 8 * b.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296); d[(e + 64 >>> 9 << 4) + 15] = a; b.sigBytes = 4 * d.length; this._process(); return this._hash }, clone: function () { var b = l.clone.call(this); b._hash = this._hash.clone(); return b } }); k.SHA1 = l._createHelper(b); k.HmacSHA1 = l._createHmacHelper(b) })();
(function (k) { for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function (q) { return 4294967296 * (q - (q | 0)) | 0 }, l = 2, b = 0; 64 > b;) { var d; a: { d = l; for (var w = k.sqrt(d), r = 2; r <= w; r++) if (!(d % r)) { d = !1; break a } d = !0 } d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++); l++ } var n = [], h = h.SHA256 = j.extend({ _doReset: function () { this._hash = new v.init(s.slice(0)) }, _doProcessBlock: function (q, h) { for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) { if (16 > e) n[e] = q[h + e] | 0; else { var m = n[e - 15], p = n[e - 2]; n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16] } m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e]; p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b); l = j; j = g; g = f; f = k + m | 0; k = b; b = d; d = c; c = m + p | 0 } a[0] = a[0] + c | 0; a[1] = a[1] + d | 0; a[2] = a[2] + b | 0; a[3] = a[3] + k | 0; a[4] = a[4] + f | 0; a[5] = a[5] + g | 0; a[6] = a[6] + j | 0; a[7] = a[7] + l | 0 }, _doFinalize: function () { var d = this._data, b = d.words, a = 8 * this._nDataBytes, c = 8 * d.sigBytes; b[c >>> 5] |= 128 << 24 - c % 32; b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296); b[(c + 64 >>> 9 << 4) + 15] = a; d.sigBytes = 4 * b.length; this._process(); return this._hash }, clone: function () { var b = j.clone.call(this); b._hash = this._hash.clone(); return b } }); g.SHA256 = j._createHelper(h); g.HmacSHA256 = j._createHmacHelper(h) })(Math);
(function(){var c=CryptoJS,k=c.enc.Utf8;c.algo.HMAC=c.lib.Base.extend({init:function(a,b){a=this._hasher=new a.init;"string"==typeof b&&(b=k.parse(b));var c=a.blockSize,e=4*c;b.sigBytes>e&&(b=a.finalize(b));b.clamp();for(var f=this._oKey=b.clone(),g=this._iKey=b.clone(),h=f.words,j=g.words,d=0;d<c;d++)h[d]^=1549556828,j[d]^=909522486;f.sigBytes=g.sigBytes=e;this.reset()},reset:function(){var a=this._hasher;a.reset();a.update(this._iKey)},update:function(a){this._hasher.update(a);return this},finalize:function(a){var b=this._hasher;a=b.finalize(a);b.reset();return b.finalize(this._oKey.clone().concat(a))}})})();
(function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a<f;a+=3)for(var d=(e[a>>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g<f;g++)b.push(c.charAt(d>>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d<e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();