<commit_before>import Q from "q";
import Workflow from "workflowit";
import Components from "./AppComponents";
import AppDelegate from "./AppDelegate";
import AppConfig from "./AppConfig";

var _instance = null;

export default class App {
    // private contructor
    constructor(components, config, delegate) {
        if (_instance) {
            throw new Error("App can only have one instance at a time. Try App.terminate() first.");
        }

        this._delegate = delegate;
        this._bootstrapper = delegate.createBootstrapper(components);
        this._events = delegate.createEventBus();
        this._workflows = delegate.createWorkflows();

        this._config = config;
        this._actions = null;

        // providers
        this._session = null;
        this._logger = null;
        this._localization = null;

        _instance = this;
        this._delegate.startup(this);
        components.onAppConfig(config);
    }

    bootstrap() {
        return Q.when(
            this._bootstrapper.bootstrap(
                this._bootstrapComplete.bind(this)
            )
        );
    }

    ready() {
        var readyEvent = { app: this, delegate: this._delegate };
        return this.events.publish.when("app.ready", readyEvent).then(() => {
            this._delegate.onReady();
        });
    }

    _bootstrapComplete(actions, providers) {
        this._actions = actions;
        this._initProviders(providers);
        this._listenForUncaughtErrors();
    }

    _initProviders({ session, logger, localization , uncaughtErrors }) {
        this._session = session;
        this._logger = logger;
        this._localization = localization;
        this._uncaughtErrors = uncaughtErrors;
    }

    _listenForUncaughtErrors() {
        this._uncaughtErrors.listen(
            (...args) => this._delegate.handleUncaughtError(...args)
        );
    }

    static instance() {
        if (!_instance) {
            throw new Error("Can not return App.instance(). App.run must be invoked first.");
        }
        return _instance;
    }

    static runAsync(components, config, delegate) {
        return Q.try(() => new App(components, config, delegate))
            .then(() => _instance.bootstrap())
            .then(() => _instance.ready())
            .catch((e) => delegate.handleRunError(e));
    }

    static run(...args) {
        App.runAsync(...args).done();
    }

    static terminate() {
        _instance = null;
    }

    get bootstrapper() {
        return this._bootstrapper;
    }

    get session() {
        return this._session;
    }
    static session() {
        return App.instance().session;
    }

    static user() {
        return App.session().user;
    }

    get config() {
        return this._config;
    }
    static get config() {
        return App.instance().config;
    }

    logger(namespace) {
        return this._logger.container(namespace);
    }
    static logger(...args) {
        return App.instance().logger(...args);
    }

    localize(path) {
        return this._localization.localize(path);
    }
    static localize(...args) {
        return App.instance().localize(...args);
    }

    dispatchAction(...args) {
        return this._actions.dispatch(...args);
    }
    static dispatchAction(...args) {
        App.instance().dispatchAction(...args)
            .done();
    }

    get events() {
        return this._events;
    }
    static get events() {
        return App.instance().events;
    }

    get workflows() {
        return this._workflows;
    }
    static get workflows() {
        return App.instance().workflows;
    }

    static get Components() { return Components; }
    static get Delegate() { return AppDelegate; }
    static get Config() { return AppConfig; }
    static get Workflow() { return Workflow; }
    static get WorkflowStep() { return Workflow.Step; }
}
<commit_msg>Fix bug localize additional args bug.<commit_after>import Q from "q";
import Workflow from "workflowit";
import Components from "./AppComponents";
import AppDelegate from "./AppDelegate";
import AppConfig from "./AppConfig";

var _instance = null;

export default class App {
    // private contructor
    constructor(components, config, delegate) {
        if (_instance) {
            throw new Error("App can only have one instance at a time. Try App.terminate() first.");
        }

        this._delegate = delegate;
        this._bootstrapper = delegate.createBootstrapper(components);
        this._events = delegate.createEventBus();
        this._workflows = delegate.createWorkflows();

        this._config = config;
        this._actions = null;

        // providers
        this._session = null;
        this._logger = null;
        this._localization = null;

        _instance = this;
        this._delegate.startup(this);
        components.onAppConfig(config);
    }

    bootstrap() {
        return Q.when(
            this._bootstrapper.bootstrap(
                this._bootstrapComplete.bind(this)
            )
        );
    }

    ready() {
        var readyEvent = { app: this, delegate: this._delegate };
        return this.events.publish.when("app.ready", readyEvent).then(() => {
            this._delegate.onReady();
        });
    }

    _bootstrapComplete(actions, providers) {
        this._actions = actions;
        this._initProviders(providers);
        this._listenForUncaughtErrors();
    }

    _initProviders({ session, logger, localization , uncaughtErrors }) {
        this._session = session;
        this._logger = logger;
        this._localization = localization;
        this._uncaughtErrors = uncaughtErrors;
    }

    _listenForUncaughtErrors() {
        this._uncaughtErrors.listen(
            (...args) => this._delegate.handleUncaughtError(...args)
        );
    }

    static instance() {
        if (!_instance) {
            throw new Error("Can not return App.instance(). App.run must be invoked first.");
        }
        return _instance;
    }

    static runAsync(components, config, delegate) {
        return Q.try(() => new App(components, config, delegate))
            .then(() => _instance.bootstrap())
            .then(() => _instance.ready())
            .catch((e) => delegate.handleRunError(e));
    }

    static run(...args) {
        App.runAsync(...args).done();
    }

    static terminate() {
        _instance = null;
    }

    get bootstrapper() {
        return this._bootstrapper;
    }

    get session() {
        return this._session;
    }
    static session() {
        return App.instance().session;
    }

    static user() {
        return App.session().user;
    }

    get config() {
        return this._config;
    }
    static get config() {
        return App.instance().config;
    }

    logger(namespace) {
        return this._logger.container(namespace);
    }
    static logger(...args) {
        return App.instance().logger(...args);
    }

    localize(path, ...args) {
        return this._localization.localize(path, ...args);
    }
    static localize(...args) {
        return App.instance().localize(...args);
    }

    dispatchAction(...args) {
        return this._actions.dispatch(...args);
    }
    static dispatchAction(...args) {
        App.instance().dispatchAction(...args)
            .done();
    }

    get events() {
        return this._events;
    }
    static get events() {
        return App.instance().events;
    }

    get workflows() {
        return this._workflows;
    }
    static get workflows() {
        return App.instance().workflows;
    }

    static get Components() { return Components; }
    static get Delegate() { return AppDelegate; }
    static get Config() { return AppConfig; }
    static get Workflow() { return Workflow; }
    static get WorkflowStep() { return Workflow.Step; }
}
<|endoftext|>


<commit_before>/**
 * @jsx React.DOM
 * @copyright Prometheus Research, LLC 2014
 */
'use strict';

var React          = require('react/addons');
var cx             = React.addons.classSet;
var Cell           = require('./Cell');
var cloneWithProps = React.addons.cloneWithProps;

var Row = React.createClass({

  render() {
    var className = cx(
      'react-grid-Row',
      `react-grid-Row--${this.props.idx % 2 === 0 ? 'even' : 'odd'}`
    );

    var style = {
      height: this.props.height,
      overflow: 'hidden'
    };

    return this.transferPropsTo(

        {React.isValidComponent(this.props.row) ?
          this.props.row :
          <this.renderCells />}

    );
  },

  renderCells() {
    var cells = [];
    var lockedCells = [];

    for (var i = 0, len = this.props.columns.length; i < len; i++) {
      var column = this.props.columns[i];
      var cell = (
        <this.renderCell
          ref={i}
          key={i}
          idx={i}
          rowIdx={this.props.idx}
          value={this.props.row[column.key || i]}
          column={column}
          height={this.props.height}
          formatter={column.formatter}
          />
      );
      if (column.locked) {
        lockedCells.push(cell);
      } else {
        cells.push(cell);
      }
    }

    return cells.concat(lockedCells);
  },

  renderCell(props) {
    if (React.isValidComponent(this.props.cellRenderer)) {
      return cloneWithProps(this.props.cellRenderer, props);
    } else {
      return this.props.cellRenderer(props);
    }
  },

  getDefaultProps() {
    return {
      cellRenderer: Cell
    };
  },

  shouldComponentUpdate(nextProps) {
    return nextProps.columns !== this.props.columns ||
      nextProps.row !== this.props.row ||
      nextProps.height !== this.props.height;
  },

  setScrollLeft(scrollLeft) {
    for (var i = 0, len = this.props.columns.length; i < len; i++) {
      if (this.props.columns[i].locked) {
        this.refs[i].setScrollLeft(scrollLeft);
      }
    }
  }
});

module.exports = Row;
<commit_msg>Massive speed increase due to not updating row component if doesnt contain active cell<commit_after>/**
 * @jsx React.DOM
 * @copyright Prometheus Research, LLC 2014
 */
'use strict';

var React          = require('react/addons');
var cx             = React.addons.classSet;
var Cell           = require('./Cell');
var cloneWithProps = React.addons.cloneWithProps;
var ColumnMetrics    = require('./ColumnMetrics');

var Row = React.createClass({

  render() {
    var className = cx(
      'react-grid-Row',
      `react-grid-Row--${this.props.idx % 2 === 0 ? 'even' : 'odd'}`
    );

    var style = {
      height: this.props.height,
      overflow: 'hidden'
    };

    return this.transferPropsTo(

        {React.isValidComponent(this.props.row) ?
          this.props.row :
          <this.renderCells />}

    );
  },

  renderCells() {
    var cells = [];
    var lockedCells = [];

    for (var i = 0, len = this.props.columns.length; i < len; i++) {
      var column = this.props.columns[i];
      var cell = (
        <this.renderCell
          ref={i}
          key={i}
          idx={i}
          rowIdx={this.props.idx}
          value={this.props.row[column.key || i]}
          column={column}
          height={this.props.height}
          formatter={column.formatter}
          />
      );
      if (column.locked) {
        lockedCells.push(cell);
      } else {
        cells.push(cell);
      }
    }

    return cells.concat(lockedCells);
  },

  renderCell(props) {
    if (React.isValidComponent(this.props.cellRenderer)) {
      return cloneWithProps(this.props.cellRenderer, props);
    } else {
      return this.props.cellRenderer(props);
    }
  },

  getDefaultProps() {
    return {
      cellRenderer: Cell
    };
  },

  shouldComponentUpdate(nextProps) {
    return !(ColumnMetrics.sameColumns(this.props.columns, nextProps.columns, ColumnMetrics.sameColumn)) ||
      this.doesRowContainSelectedCell() ||
      this.doesRowContainSelectedCell(nextProps) ||
      nextProps.row !== this.props.row  ||
      nextProps.height !== this.props.height;
  },

  setScrollLeft(scrollLeft) {
    for (var i = 0, len = this.props.columns.length; i < len; i++) {
      if (this.props.columns[i].locked) {
        this.refs[i].setScrollLeft(scrollLeft);
      }
    }
  },

  doesRowContainSelectedCell(propsToCheck){
    var props = propsToCheck || this.props;
    var cell = cell || props.cellRenderer;
    if(cell.props.selected && cell.props.selected.rowIdx === props.idx){
      return true;
    }else{
      return false;
    }
  }

});

module.exports = Row;
<|endoftext|>


<commit_before>/*
 * Copyright © 2007 Dominic Mitchell
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:

 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the Dominic Mitchell nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * An URI datatype.  Based upon examples in RFC3986.
 *
 * TODO %-escaping
 * TODO split apart authority
 * TODO split apart query_string (on demand, anyway)
 *
 * @(#) $Id$
 */

// Constructor for the URI object.  Parse a string into its components.
function URI(str) {
    if (!str) str = "";
    // Based on the regex in RFC2396 Appendix B.
    var parser = /^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;
    var result = str.match(parser);
    this.scheme    = result[1] || null;
    this.authority = result[2] || null;
    this.path      = result[3] || null;
    this.query     = result[4] || null;
    this.fragment  = result[5] || null;
}

// Restore the URI to it's stringy glory.
URI.prototype.toString = function () {
    var str = "";
    if (this.scheme) {
        str += this.scheme + ":";
    }
    if (this.authority) {
        str += "//" + this.authority;
    }
    if (this.path) {
        str += this.path;
    }
    if (this.query) {
        str += "?" + this.query;
    }
    if (this.fragment) {
        str += "#" + this.fragment;
    }
    return str;
};

// Introduce a new scope to define some private helper functions.
(function () {
    // RFC3986 §5.2.3 (Merge Paths)
    function merge(base, rel_path) {
        var dirname = /^(.*)\//;
        if (base.authority && !base.path) {
            return "/" + rel_path;
        }
        else {
            return base.path.match(dirname)[0] + rel_path;
        }
    }

    // Match two path segments, where the second is ".." and the first must
    // not be "..".
    var DoubleDot = /\/((?!\.\.\/)[^\/]*)\/\.\.\//;

    function remove_dot_segments(path) {
        if (!path) return "";
        // Remove any single dots
        var newpath = path.replace(/\/\.\//g, '/');
        // Remove any trailing single dots.
        newpath = newpath.replace(/\/\.$/, '/');
        // Remove any double dots and the path previous.  NB: We can't use
        // the "g", modifier because we are changing the string that we're
        // matching over.
        while (newpath.match(DoubleDot)) {
            newpath = newpath.replace(DoubleDot, '/');
        }
        // Remove any trailing double dots.
        newpath = newpath.replace(/\/([^\/]*)\/\.\.$/, '/');
        // If there are any remaining double dot bits, then they're wrong
        // and must be nuked.  Again, we can't use the g modifier.
        while (newpath.match(/\/\.\.\//)) {
            newpath = newpath.replace(/\/\.\.\//, '/');
        }
        return newpath;
    }

    // RFC3986 §5.2.2. Transform References;
    URI.prototype.resolve = function (base) {
        var target = new URI();
        if (this.scheme) {
            target.scheme    = this.scheme;
            target.authority = this.authority;
            target.path      = remove_dot_segments(this.path);
            target.query     = this.query;
        }
        else {
            if (this.authority) {
                target.authority = this.authority;
                target.path      = remove_dot_segments(this.path);
                target.query     = this.query;
            }
            else {
                // XXX Original spec says "if defined and empty"…;
                if (!this.path) {
                    target.path = base.path;
                    if (this.query) {
                        target.query = this.query;
                    }
                    else {
                        target.query = base.query;
                    }
                }
                else {
                    if (this.path.charAt(0) === '/') {
                        target.path = remove_dot_segments(this.path);
                    } else {
                        target.path = merge(base, this.path);
                        target.path = remove_dot_segments(target.path);
                    }
                    target.query = this.query;
                }
                target.authority = base.authority;
            }
            target.scheme = base.scheme;
        }

        target.fragment = this.fragment;

        return target;
    };
})();
<commit_msg>add query_form function from Perl URI<commit_after>/*
 * Copyright © 2007 Dominic Mitchell
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:

 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the Dominic Mitchell nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * An URI datatype.  Based upon examples in RFC3986.
 *
 * TODO %-escaping
 * TODO split apart authority
 * TODO split apart query_string (on demand, anyway)
 *
 * @(#) $Id$
 */

// Constructor for the URI object.  Parse a string into its components.
function URI(str) {
    if (!str) str = "";
    // Based on the regex in RFC2396 Appendix B.
    var parser = /^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;
    var result = str.match(parser);
    this.scheme    = result[1] || null;
    this.authority = result[2] || null;
    this.path      = result[3] || null;
    this.query     = result[4] || null;
    this.fragment  = result[5] || null;
}

// Restore the URI to it's stringy glory.
URI.prototype.toString = function () {
    var str = "";
    if (this.scheme) {
        str += this.scheme + ":";
    }
    if (this.authority) {
        str += "//" + this.authority;
    }
    if (this.path) {
        str += this.path;
    }
    if (this.query) {
        str += "?" + this.query;
    }
    if (this.fragment) {
        str += "#" + this.fragment;
    }
    return str;
};

// Introduce a new scope to define some private helper functions.
(function () {
    // RFC3986 §5.2.3 (Merge Paths)
    function merge(base, rel_path) {
        var dirname = /^(.*)\//;
        if (base.authority && !base.path) {
            return "/" + rel_path;
        }
        else {
            return base.path.match(dirname)[0] + rel_path;
        }
    }

    // Match two path segments, where the second is ".." and the first must
    // not be "..".
    var DoubleDot = /\/((?!\.\.\/)[^\/]*)\/\.\.\//;

    function remove_dot_segments(path) {
        if (!path) return "";
        // Remove any single dots
        var newpath = path.replace(/\/\.\//g, '/');
        // Remove any trailing single dots.
        newpath = newpath.replace(/\/\.$/, '/');
        // Remove any double dots and the path previous.  NB: We can't use
        // the "g", modifier because we are changing the string that we're
        // matching over.
        while (newpath.match(DoubleDot)) {
            newpath = newpath.replace(DoubleDot, '/');
        }
        // Remove any trailing double dots.
        newpath = newpath.replace(/\/([^\/]*)\/\.\.$/, '/');
        // If there are any remaining double dot bits, then they're wrong
        // and must be nuked.  Again, we can't use the g modifier.
        while (newpath.match(/\/\.\.\//)) {
            newpath = newpath.replace(/\/\.\.\//, '/');
        }
        return newpath;
    }

    // RFC3986 §5.2.2. Transform References;
    URI.prototype.resolve = function (base) {
        var target = new URI();
        if (this.scheme) {
            target.scheme    = this.scheme;
            target.authority = this.authority;
            target.path      = remove_dot_segments(this.path);
            target.query     = this.query;
        }
        else {
            if (this.authority) {
                target.authority = this.authority;
                target.path      = remove_dot_segments(this.path);
                target.query     = this.query;
            }
            else {
                // XXX Original spec says "if defined and empty"…;
                if (!this.path) {
                    target.path = base.path;
                    if (this.query) {
                        target.query = this.query;
                    }
                    else {
                        target.query = base.query;
                    }
                }
                else {
                    if (this.path.charAt(0) === '/') {
                        target.path = remove_dot_segments(this.path);
                    } else {
                        target.path = merge(base, this.path);
                        target.path = remove_dot_segments(target.path);
                    }
                    target.query = this.query;
                }
                target.authority = base.authority;
            }
            target.scheme = base.scheme;
        }

        target.fragment = this.fragment;

        return target;
    };

    var query_decode = function(txt) {
        return decodeURI(txt.replace(/\+/, ' '));
    };

    URI.prototype.query_form = function () {
        var kvp = this.query.split(/[&;]/);
        var rv = ({});
        for (var i = 0; i < kvp.length; ++i)
        {
            var kva = kvp[i].split(/\=/);
            var key = query_decode(kva[0]);
            var val = query_decode(kva[1]);
            rv[key] = val;
        }
        return rv;
    };
})();
<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var url = require('url');
var fs = require('fs');

var async = require('async');
var request = require('request');

request = request.defaults({
    json: true
});

var getIssues = exports.getIssues = function (username, reponame, token, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues'),
        query: {
            access_token: token,
            state: 'all'
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        headers: {
            'User-Agent': 'request'
        }
    }, function (err, response, issues) {
        if (err) {
            console.log(err);
        } else {
            callback(err, issues);
        }
    });
};

exports.transferMultiRepoIssuesToFile = function (username, reponames, token, targetDirectory, callback) {
    var iterator = function (repo, callback) {
        getIssues(username, repo, token, function (err, issues) {
            if (err) {
                callback(err);
            } else {
                var filePath = path.join(targetDirectory, repo + '.json');
                fs.writeFile(filePath, JSON.stringify(issues, undefined, 4), function (err) {
                    callback(err);
                });
            }
        });
    };

    async.each(reponames, iterator, callback);
};

var prepareIssues = exports.prepareIssues = function (srcIssues) {
    var result = srcIssues.map(function (srcIssue) {
        var issue = {
            title: srcIssue.title,
            body: srcIssue.body,
            assignee: srcIssue.user.login,
            close: (srcIssue.state === 'closed')
        };
        if (srcIssue.labels) {
            issue.labels = srcIssue.labels.map(function (label) {
                return label.name;
            });
        }
        return issue;
    });
    return result;
};

var createIssue = exports.createIssue = function (username, reponame, token, issue, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues'),
        query: {
            access_token: token
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        method: 'POST',
        headers: {
            'User-Agent': 'request'
        },
        body: issue
    }, function (err, response, body) {
        callback(err, body);
    });
};

var closeIssue = exports.closeIssue = function (username, reponame, token, issueNumber, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues', issueNumber),
        query: {
            access_token: token
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        method: 'PATCH',
        headers: {
            'User-Agent': 'request'
        },
        body: {
            state: 'closed'
        }
    }, function (err, response, body) {
        callback(err, body);
    });
};

exports.moveIssuesToRepo = function (issues, username, reponame, token, callback) {
    var newIssues = prepareIssues(issues);

    var fn = function (newIssue, cb) {
        var close = newIssue.close;
        delete newIssue.close;
        createIssue(username, reponame, token, newIssue, function (err, createdIssue) {
            if (err || !close) {
                cb(err, createdIssue);
            } else {
                closeIssue(username, reponame, token, createdIssue.number.toString(), cb);
            }
        });
    };

    async.mapSeries(newIssues, fn, callback);
};
<commit_msg>comments are added to new issues<commit_after>'use strict';

var path = require('path');
var url = require('url');
var fs = require('fs');

var async = require('async');
var request = require('request');

request = request.defaults({
    json: true
});

var getIssues = exports.getIssues = function (username, reponame, token, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues'),
        query: {
            access_token: token,
            state: 'all'
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        headers: {
            'User-Agent': 'request'
        }
    }, function (err, response, issues) {
        if (err) {
            console.log(err);
        } else {
            callback(err, issues);
        }
    });
};

var getComments = exports.getComments = function (username, reponame, token, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues', 'comments'),
        query: {
            access_token: token
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        headers: {
            'User-Agent': 'request'
        }
    }, function (err, response, comments) {
        if (err) {
            console.log(err);
        } else {
            callback(err, comments);
        }
    });
};

exports.transferMultiRepoIssuesToFile = function (username, reponames, token, targetDirectory, callback) {
    var iterator = function (repo, callback) {
        getIssues(username, repo, token, function (err, issues) {
            if (err) {
                callback(err);
            } else {
                var filePath = path.join(targetDirectory, repo + '.json');
                fs.writeFile(filePath, JSON.stringify(issues, undefined, 4), function (err) {
                    callback(err);
                });
            }
        });
    };

    async.each(reponames, iterator, callback);
};

exports.transferMultiRepoIssueCommentsToFile = function (username, reponames, token, targetDirectory, callback) {
    var iterator = function (repo, callback) {
        getComments(username, repo, token, function (err, comments) {
            if (err) {
                callback(err);
            } else {
                var filePath = path.join(targetDirectory, repo + '_comments.json');
                fs.writeFile(filePath, JSON.stringify(comments, undefined, 4), function (err) {
                    callback(err);
                });
            }
        });
    };

    async.each(reponames, iterator, callback);
};

var prepareIssues = exports.prepareIssues = function (srcIssues, srcComments) {
    var result = srcIssues.map(function (srcIssue) {
        var issue = {
            title: srcIssue.title,
            body: srcIssue.body,
            assignee: srcIssue.user.login,
            close: (srcIssue.state === 'closed')
        };
        if (srcIssue.labels) {
            issue.labels = srcIssue.labels.map(function (label) {
                return label.name;
            });
        }
        return issue;
    });
    return result;
};

var addRepoToCommit = function (username, reponame, comment) {
    var piece = comment;
    var n = comment.length;
    var result = '';
    while (true) {
        var location = piece.search(/\b[0-9A-Fa-f]{40}\b/g);
        if (location > -1) {
            var index = location + 40;
            result += piece.substring(0, location);
            result += username + '/' + reponame + '@' + piece.substring(location, index);
            piece = piece.substring(index, n);
        } else {
            result += piece;
            return result;
        }
    }
};

var prepareIssuesForMove = exports.prepareIssuesForMove = function (username, reponame, issues, comments) {
    var issueUrlToIndex = issues.reduce(function (r, issue, index) {
        var url = issue.url;
        r[url] = index;
        return r;
    }, {});

    var result = issues.map(function (issue) {
        var r = {
            issue: {
                title: issue.title,
                body: issue.body,
                assignee: issue.user.login
            },
            closed: (issue.state === 'closed'),
            comments: []
        };
        if (issue.labels) {
            r.issue.labels = issue.labels.map(function (label) {
                return label.name;
            });
        }
        return r;
    });

    comments.forEach(function (comment) {
        var issueUrl = comment.issue_url;
        var index = issueUrlToIndex[issueUrl];
        var newComment = addRepoToCommit(username, reponame, comment.body);
        result[index].comments.unshift(newComment);
    });

    return result;
};

var createIssue = exports.createIssue = function (username, reponame, token, issue, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues'),
        query: {
            access_token: token
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        method: 'POST',
        headers: {
            'User-Agent': 'request'
        },
        body: issue
    }, function (err, response, body) {
        callback(err, body);
    });
};

var closeIssue = exports.closeIssue = function (username, reponame, token, issueNumber, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues', issueNumber),
        query: {
            access_token: token
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        method: 'PATCH',
        headers: {
            'User-Agent': 'request'
        },
        body: {
            state: 'closed'
        }
    }, function (err, response, body) {
        callback(err, body);
    });
};

var addComment = exports.closeIssue = function (username, reponame, token, issueNumber, comment, callback) {
    var urlObj = {
        protocol: 'https',
        hostname: 'api.github.com',
        pathname: path.join('repos', username, reponame, 'issues', issueNumber, 'comments'),
        query: {
            access_token: token
        }
    };
    var urlstring = url.format(urlObj);
    request({
        url: urlstring,
        method: 'POST',
        headers: {
            'User-Agent': 'request'
        },
        body: {
            body: comment
        }
    }, function (err, response, body) {
        callback(err, body);
    });
};

var updateIssue = function (issueNumber, issueInfo, username, reponame, token, callback) {
    var fns = [];
    if (issueInfo.closed) {
        fns.push(function (cb) {
            closeIssue(username, reponame, token, issueNumber, cb);
        });
    }
    var cmt = function(comment) {
        return function (cb) {
            console.log('issue ' + issueNumber);
            addComment(username, reponame, token, issueNumber, comment, cb);
        }
    };
    issueInfo.comments.forEach(function(comment) {
        fns.push(cmt(comment));
    });
    console.log(fns.length);
    if (fns.length) {
        async.series(fns, callback);
    } else {
        callback(null);
    }

};

exports.moveIssuesToRepo = function (issueInfos, username, reponame, token, callback) {
    var fn = function (issueInfo, cb) {
        createIssue(username, reponame, token, issueInfo.issue, function (err, issue) {
            if (err) {
                cb(err);
            } else {
                updateIssue(issue.number.toString(), issueInfo, username, reponame, token, cb);
            }
        });
    };

    async.mapSeries(issueInfos, fn, callback);
};
<|endoftext|>


<commit_before>"use strict";

/**
* api.js
* Copyright(c) 2014 Aaron Hedges <aaron@dashron.com>
* MIT Licensed
 */

var url_module = require('url');
var Promise = require('bluebird');
var roads = require('../index');
var Response = roads.Response;

var API = module.exports = function API (rootResource, representations) {
	this.rootResource = rootResource;

	// ensure we have at least one resource to route to
	if (!this.rootResource) {
		throw new Error('You must configure a root resource before making API requests');
	}
};

API.prototype.rootResource = null;
API.prototype._onError = null;

/**
 *
 * @param  {Function} fn At the moment this can not be a coroutine. That may change in the future
 * @return {[type]}      [description]
 */
API.prototype.onError = function (fn) {
	this._onError = fn;
};

/**
 * Make a request into the API
 *
 * @param  string method
 * @param  string url
 * @param  string body
 * @param  array headers
 * @return string
 */
API.prototype.request = function (method, url, body, headers) {
	url = url_module.parse(url, true);
	return Promise.coroutine(this.locateRoute(url, method))(url, body, headers);
};

/**
 * Find the appropriate route for the requested pathname and method
 *
 * @param  string pathname [description]
 * @param  sring method   [description]
 * @return function
 */
API.prototype.locateRoute = function (parsed_url, method) {
	var api = this;
	var resource = this.locateResource(parsed_url);

	// If we could not find the appropriate resource, 404 not found
	if (!resource) {
		return function* () {
			// we throw this so that it lines up with how all errors are handled
			throw new roads.HttpError(parsed_url.pathname, 404);
		}
	}

	// If we could not find the appropriate route, 405 Method not allowed
	if (!resource[method]) {
		return function* () {
			// we throw this so that it lines up with how all errors are handled
			throw new roads.HttpError(resource.getValidMethods(), 405);
		}
	}

	return resource[method].bind(resource);
}

/**
 * Route the pathname directly to a resource object
 *
 * @param  string pathname
 * @return Resource
 */
API.prototype.locateResource = function (parsed_url) {
	var pathname = parsed_url.pathname.replace(/\/$/, '');

	// this will cause issues
	var parts = pathname.split('/');

	// the uri starts with a forward slash, so we will have an empty string at the start of the array
	var part = parts.shift();

	var resource = this.rootResource;
	var resource_info = null;

	// Define the object that will hold all url arguments
	if (!parsed_url.args) {
		parsed_url.args = {};
	}

	// loop through every part separated by slashes and incrementally check them against the routes
	while (parts.length) {
		part = parts.shift();
		resource_info = resource.getResourceKey(part);

		if (!resource_info) {
			return false;
		} else {
			resource = resource_info.route;
			// apply the route parts to the url arguments field
			if (resource_info.key) {
				parsed_url.args[resource_info.key] = resource_info.value;
			}
		}
	}

	return resource;
}

/**
 * Helper function so the api can be thrown directly into http.createServer
 *
 * @param  ClientRequest http_request
 * @param  ServerResponse http_response
 */
API.prototype.server = function (http_request, http_response) {
	var api = this;
	var body = '';

	http_request.on('readable', function () {
  		var chunk;
		while (null !== (chunk = http_request.read())) {
			body += chunk;
		}
	});

	http_request.on('end', function () {

		// execute the api logic and retrieve the appropriate response object
		api.request(http_request.method, http_request.url, body, http_request.headers)
			.then(function (response) {
				response.writeTo(http_response)
					.catch(function (err) {
						api._onError(err).writeTo(http_response);
					});
			})
			.catch(function (err) {
				// route errors go to the unknown error representation
				api._onError(err).writeTo(http_response);
			});
	});

	// server request errors go to the unknown error representation
	http_request.on('error', function (err) {
		api._onError(err).writeTo(http_response);
	});
}<commit_msg>remove old reference to representations<commit_after>"use strict";

/**
* api.js
* Copyright(c) 2014 Aaron Hedges <aaron@dashron.com>
* MIT Licensed
 */

var url_module = require('url');
var Promise = require('bluebird');
var roads = require('../index');
var Response = roads.Response;

var API = module.exports = function API (rootResource) {
	this.rootResource = rootResource;

	// ensure we have at least one resource to route to
	if (!this.rootResource) {
		throw new Error('You must configure a root resource before making API requests');
	}
};

API.prototype.rootResource = null;
API.prototype._onError = null;

/**
 *
 * @param  {Function} fn At the moment this can not be a coroutine. That may change in the future
 * @return {[type]}      [description]
 */
API.prototype.onError = function (fn) {
	this._onError = fn;
};

/**
 * Make a request into the API
 *
 * @param  string method
 * @param  string url
 * @param  string body
 * @param  array headers
 * @return string
 */
API.prototype.request = function (method, url, body, headers) {
	url = url_module.parse(url, true);
	return Promise.coroutine(this.locateRoute(url, method))(url, body, headers);
};

/**
 * Find the appropriate route for the requested pathname and method
 *
 * @param  string pathname [description]
 * @param  sring method   [description]
 * @return function
 */
API.prototype.locateRoute = function (parsed_url, method) {
	var api = this;
	var resource = this.locateResource(parsed_url);

	// If we could not find the appropriate resource, 404 not found
	if (!resource) {
		return function* () {
			// we throw this so that it lines up with how all errors are handled
			throw new roads.HttpError(parsed_url.pathname, 404);
		}
	}

	// If we could not find the appropriate route, 405 Method not allowed
	if (!resource[method]) {
		return function* () {
			// we throw this so that it lines up with how all errors are handled
			throw new roads.HttpError(resource.getValidMethods(), 405);
		}
	}

	return resource[method].bind(resource);
}

/**
 * Route the pathname directly to a resource object
 *
 * @param  string pathname
 * @return Resource
 */
API.prototype.locateResource = function (parsed_url) {
	var pathname = parsed_url.pathname.replace(/\/$/, '');

	// this will cause issues
	var parts = pathname.split('/');

	// the uri starts with a forward slash, so we will have an empty string at the start of the array
	var part = parts.shift();

	var resource = this.rootResource;
	var resource_info = null;

	// Define the object that will hold all url arguments
	if (!parsed_url.args) {
		parsed_url.args = {};
	}

	// loop through every part separated by slashes and incrementally check them against the routes
	while (parts.length) {
		part = parts.shift();
		resource_info = resource.getResourceKey(part);

		if (!resource_info) {
			return false;
		} else {
			resource = resource_info.route;
			// apply the route parts to the url arguments field
			if (resource_info.key) {
				parsed_url.args[resource_info.key] = resource_info.value;
			}
		}
	}

	return resource;
}

/**
 * Helper function so the api can be thrown directly into http.createServer
 *
 * @param  ClientRequest http_request
 * @param  ServerResponse http_response
 */
API.prototype.server = function (http_request, http_response) {
	var api = this;
	var body = '';

	http_request.on('readable', function () {
  		var chunk;
		while (null !== (chunk = http_request.read())) {
			body += chunk;
		}
	});

	http_request.on('end', function () {

		// execute the api logic and retrieve the appropriate response object
		api.request(http_request.method, http_request.url, body, http_request.headers)
			.then(function (response) {
				response.writeTo(http_response)
					.catch(function (err) {
						api._onError(err).writeTo(http_response);
					});
			})
			.catch(function (err) {
				// route errors go to the unknown error representation
				api._onError(err).writeTo(http_response);
			});
	});

	// server request errors go to the unknown error representation
	http_request.on('error', function (err) {
		api._onError(err).writeTo(http_response);
	});
}<|endoftext|>


<commit_before>'use strict';

const bunyan = require('bunyan');
const PromiseEventEmitter = require('promise-events');
const { Status } = require('flora-cluster');
const { NotFoundError, RequestError } = require('flora-errors');
const moment = require('moment-timezone');
const has = require('has');

const asciiArtProfile = require('./ascii-art-profile');
const ResourceProcessor = require('./resource-processor');
const Response = require('./response');
const defaultResource = require('./default-resource');

/**
 * @event Api#init
 * @description Emitted after data sources have been initialized and the instance is ready.
 */

/**
 * @event Api#close
 * @description Emitted when the instance has closed.
 */

/**
 * @event Api#request
 * @description Emitted before a request is handled.
 * @type {Object}
 * @property {Request} request - The request that is being executed.
 * @property {Response} response - The response object that is being passed to the request handler.
 */

/**
 * @event Api#response
 * @description Emitted before a response is returned by the execute() method.
 * @type {Object}
 * @property {Request} request - The request was executed.
 * @property {Response} response - The response object that was returned.
 */

/**
 * The main API entry point.
 * Provides the "execute" function that actually executes a {@link Request|Request}
 */
class Api extends PromiseEventEmitter {
    constructor() {
        super();

        this.config = null;
        this.resourceProcessor = null;
        this.log = null;
        this.dataSources = {};
        this.plugins = {};
        this.clusterWorker = null;

        this._initialized = false;
        this._defaultResourceInstance = null;
    }

    /**
     * Initialize and handle the config.
     *
     * @param {Object} [config] - API configuration
     * @param {Object} [config.dataSources] - Available data sources
     * @param {Object} [config.log] - {@link https://github.com/trentm/node-bunyan|Bunyan} logger instance
     * @returns {Promise}
     * @fires Api#init
     */
    async init(config) {
        this.config = config || {};
        this.log = this.config.log || bunyan.createLogger({ name: 'flora', level: 'debug' });
        this.status = this.clusterWorker ? this.clusterWorker.status : new Status();

        if (this.config.dataSources) {
            this.log.debug('Registering data sources');
            const status = this.status.child('dataSources');

            Object.keys(this.config.dataSources).forEach((name) => {
                const ds = config.dataSources[name];
                this.log.trace(`Registering data source "${name}"`);
                if (typeof ds !== 'object') {
                    throw new Error(`Data source configuration for "${name}" needs to be an object`);
                }
                if (typeof ds.constructor !== 'function') {
                    throw new Error(`Data source configuration for "${name}" does not have a constructor function`);
                }
                ds.options = ds.options || {};
                ds.options._status = status.child(name);
                this.dataSources[name] = new ds.constructor(this, ds.options);
            });
        }

        if (this.config.timezone) {
            const zone = moment.tz.zone(this.config.timezone);
            if (!zone) throw new Error(`Timezone "${this.config.timezone}" does not exist`);
            this.log.debug(`Using timezone "${zone.name}"`);
        } else {
            this.log.warn('No timezone is set, using default "UTC"');
        }

        this.resourceProcessor = new ResourceProcessor(this);
        try {
            await this.resourceProcessor.init(this.config);
        } catch (err) {
            await this.emit('close');
            throw err;
        }

        this._defaultResourceInstance = defaultResource(this);
        this._initialized = true;
        return this.emit('init');
    }

    /**
     * Gracefully shutdown the instance.
     *
     * @returns {Promise}
     * @fires Api#close
     */
    async close() {
        if (!this._initialized) {
            // Tried to close the instance before `init` was done
            return new Promise((resolve, reject) => {
                this.once('close', () => reject(new Error('Not running')));
                this.emit('close');
            });
        }

        this.log.info('Closing API instance');

        await Promise.all(Object.keys(this.dataSources).map(async (name) => {
            const ds = this.dataSources[name];
            if (!ds.close) return;
            try {
                await ds.close();
            } catch (err) {
                this.log.warn(err, `Error closing data source "${name}"`);
            }
        }));

        this.log.info('Closed API instance');
        return this.emit('close');
    }

    /**
     * Execute a request and return the response.
     *
     * @param {Request} request - Request to process
     * @returns {Promise<Object>}
     * @fires Api#request
     * @fires Api#response
     */
    async execute(request) {
        if (!this._initialized) throw new Error('Not initialized');

        const response = new Response(request);

        this.log.debug(request, 'executing request');

        await this.emit('request', { request, response });

        const resource = this.getResource(request.resource);
        if (!resource) throw new NotFoundError(`Unknown resource "${request.resource}" in request`);

        if (!resource.actions || !has(resource.actions, request.action)) {
            throw new RequestError(`Action "${request.action}" is not implemented`);
        }

        let ret;
        try {
            const method = request.format === 'json' ? 'default' : request.format;
            if (method === 'default' && typeof resource.actions[request.action] === 'function') {
                ret = await (resource.actions[request.action])(request, response);
            } else {
                if (typeof resource.actions[request.action] !== 'object') {
                    throw new RequestError(`Invalid format "${request.format}" for action "${request.action}"`);
                }
                if (!has(resource.actions[request.action], method)
                    || typeof resource.actions[request.action][method] !== 'function') {
                    throw new RequestError(`Invalid format "${request.format}" for action "${request.action}"`);
                }
                ret = await (resource.actions[request.action][method])(request, response);
            }
        } catch (err) {
            if (err.httpStatusCode && err.httpStatusCode < 500) {
                this.log.info({ err, req: request._httpRequest }, 'Request error');
            } else {
                this.log.error({ err, req: request._httpRequest }, 'Server error');
            }
            throw err;
        }

        if (typeof ret !== 'undefined') response.data = ret;

        // Stop profiler
        request._profiler.end();
        response.meta.duration = request._profiler.getDuration();

        if (request._profile === 'raw' || request._profile === '1') {
            const profile = this.request._profiler.report();

            if (request._profile === 'raw') {
                response.meta.profile = profile;
            } else {
                response.meta.profile = asciiArtProfile(profile, response.meta.duration, 100);
            }
        }

        // Extension: "response" (resource scope)
        if (resource.extensions && resource.extensions.response) {
            await resource.extensions.response({ request, response });
        }

        await this.emit('response', { request, response });

        return response;
    }

    /**
     * Retrieve the resource instance (or the default resource).
     * Returns null if the resource was not found.
     *
     * @param {String} resource - Resource name
     * @returns {Object|null}
     */
    getResource(resource) {
        if (!this.resourceProcessor.resourceConfigs[resource]) return null;
        return this.resourceProcessor.resourceConfigs[resource].instance
            || this._defaultResourceInstance;
    }

    /**
     * Register a plugin.
     *
     * The plugin has to be a function, which is called with parameters (api, options).
     *
     * @param {string} name - Plugin name
     * @param {function} plugin - Plugin function
     * @param {Object} [options] - Configuration options that are passed to the function
     */
    register(name, plugin, options) {
        if (this.plugins[name]) throw new Error(`Plugin "${name}" already registered.`);
        if (typeof plugin !== 'function') throw new Error('Plugin needs to be a function');

        this.plugins[name] = plugin(this, options);
    }

    /**
     * Retrieve plugin data/instance.
     *
     * Returns whatever the plugin function returned at registration time.
     *
     * @param {string} name - Plugin name
     * @throws Error when no plugin is registered with this name
     * @returns {*}
     */
    getPlugin(name) {
        if (!has(this.plugins, name)) throw new Error(`Plugin "${name}" is not registered`);
        return this.plugins[name];
    }
}

module.exports = Api;
<commit_msg>Revert default log level to 'info'<commit_after>'use strict';

const bunyan = require('bunyan');
const PromiseEventEmitter = require('promise-events');
const { Status } = require('flora-cluster');
const { NotFoundError, RequestError } = require('flora-errors');
const moment = require('moment-timezone');
const has = require('has');

const asciiArtProfile = require('./ascii-art-profile');
const ResourceProcessor = require('./resource-processor');
const Response = require('./response');
const defaultResource = require('./default-resource');

/**
 * @event Api#init
 * @description Emitted after data sources have been initialized and the instance is ready.
 */

/**
 * @event Api#close
 * @description Emitted when the instance has closed.
 */

/**
 * @event Api#request
 * @description Emitted before a request is handled.
 * @type {Object}
 * @property {Request} request - The request that is being executed.
 * @property {Response} response - The response object that is being passed to the request handler.
 */

/**
 * @event Api#response
 * @description Emitted before a response is returned by the execute() method.
 * @type {Object}
 * @property {Request} request - The request was executed.
 * @property {Response} response - The response object that was returned.
 */

/**
 * The main API entry point.
 * Provides the "execute" function that actually executes a {@link Request|Request}
 */
class Api extends PromiseEventEmitter {
    constructor() {
        super();

        this.config = null;
        this.resourceProcessor = null;
        this.log = null;
        this.dataSources = {};
        this.plugins = {};
        this.clusterWorker = null;

        this._initialized = false;
        this._defaultResourceInstance = null;
    }

    /**
     * Initialize and handle the config.
     *
     * @param {Object} [config] - API configuration
     * @param {Object} [config.dataSources] - Available data sources
     * @param {Object} [config.log] - {@link https://github.com/trentm/node-bunyan|Bunyan} logger instance
     * @returns {Promise}
     * @fires Api#init
     */
    async init(config) {
        this.config = config || {};
        this.log = this.config.log || bunyan.createLogger({ name: 'flora' });
        this.status = this.clusterWorker ? this.clusterWorker.status : new Status();

        if (this.config.dataSources) {
            this.log.debug('Registering data sources');
            const status = this.status.child('dataSources');

            Object.keys(this.config.dataSources).forEach((name) => {
                const ds = config.dataSources[name];
                this.log.trace(`Registering data source "${name}"`);
                if (typeof ds !== 'object') {
                    throw new Error(`Data source configuration for "${name}" needs to be an object`);
                }
                if (typeof ds.constructor !== 'function') {
                    throw new Error(`Data source configuration for "${name}" does not have a constructor function`);
                }
                ds.options = ds.options || {};
                ds.options._status = status.child(name);
                this.dataSources[name] = new ds.constructor(this, ds.options);
            });
        }

        if (this.config.timezone) {
            const zone = moment.tz.zone(this.config.timezone);
            if (!zone) throw new Error(`Timezone "${this.config.timezone}" does not exist`);
            this.log.debug(`Using timezone "${zone.name}"`);
        } else {
            this.log.warn('No timezone is set, using default "UTC"');
        }

        this.resourceProcessor = new ResourceProcessor(this);
        try {
            await this.resourceProcessor.init(this.config);
        } catch (err) {
            await this.emit('close');
            throw err;
        }

        this._defaultResourceInstance = defaultResource(this);
        this._initialized = true;
        return this.emit('init');
    }

    /**
     * Gracefully shutdown the instance.
     *
     * @returns {Promise}
     * @fires Api#close
     */
    async close() {
        if (!this._initialized) {
            // Tried to close the instance before `init` was done
            return new Promise((resolve, reject) => {
                this.once('close', () => reject(new Error('Not running')));
                this.emit('close');
            });
        }

        this.log.info('Closing API instance');

        await Promise.all(Object.keys(this.dataSources).map(async (name) => {
            const ds = this.dataSources[name];
            if (!ds.close) return;
            try {
                await ds.close();
            } catch (err) {
                this.log.warn(err, `Error closing data source "${name}"`);
            }
        }));

        this.log.info('Closed API instance');
        return this.emit('close');
    }

    /**
     * Execute a request and return the response.
     *
     * @param {Request} request - Request to process
     * @returns {Promise<Object>}
     * @fires Api#request
     * @fires Api#response
     */
    async execute(request) {
        if (!this._initialized) throw new Error('Not initialized');

        const response = new Response(request);

        this.log.debug(request, 'executing request');

        await this.emit('request', { request, response });

        const resource = this.getResource(request.resource);
        if (!resource) throw new NotFoundError(`Unknown resource "${request.resource}" in request`);

        if (!resource.actions || !has(resource.actions, request.action)) {
            throw new RequestError(`Action "${request.action}" is not implemented`);
        }

        let ret;
        try {
            const method = request.format === 'json' ? 'default' : request.format;
            if (method === 'default' && typeof resource.actions[request.action] === 'function') {
                ret = await (resource.actions[request.action])(request, response);
            } else {
                if (typeof resource.actions[request.action] !== 'object') {
                    throw new RequestError(`Invalid format "${request.format}" for action "${request.action}"`);
                }
                if (!has(resource.actions[request.action], method)
                    || typeof resource.actions[request.action][method] !== 'function') {
                    throw new RequestError(`Invalid format "${request.format}" for action "${request.action}"`);
                }
                ret = await (resource.actions[request.action][method])(request, response);
            }
        } catch (err) {
            if (err.httpStatusCode && err.httpStatusCode < 500) {
                this.log.info({ err, req: request._httpRequest }, 'Request error');
            } else {
                this.log.error({ err, req: request._httpRequest }, 'Server error');
            }
            throw err;
        }

        if (typeof ret !== 'undefined') response.data = ret;

        // Stop profiler
        request._profiler.end();
        response.meta.duration = request._profiler.getDuration();

        if (request._profile === 'raw' || request._profile === '1') {
            const profile = this.request._profiler.report();

            if (request._profile === 'raw') {
                response.meta.profile = profile;
            } else {
                response.meta.profile = asciiArtProfile(profile, response.meta.duration, 100);
            }
        }

        // Extension: "response" (resource scope)
        if (resource.extensions && resource.extensions.response) {
            await resource.extensions.response({ request, response });
        }

        await this.emit('response', { request, response });

        return response;
    }

    /**
     * Retrieve the resource instance (or the default resource).
     * Returns null if the resource was not found.
     *
     * @param {String} resource - Resource name
     * @returns {Object|null}
     */
    getResource(resource) {
        if (!this.resourceProcessor.resourceConfigs[resource]) return null;
        return this.resourceProcessor.resourceConfigs[resource].instance
            || this._defaultResourceInstance;
    }

    /**
     * Register a plugin.
     *
     * The plugin has to be a function, which is called with parameters (api, options).
     *
     * @param {string} name - Plugin name
     * @param {function} plugin - Plugin function
     * @param {Object} [options] - Configuration options that are passed to the function
     */
    register(name, plugin, options) {
        if (this.plugins[name]) throw new Error(`Plugin "${name}" already registered.`);
        if (typeof plugin !== 'function') throw new Error('Plugin needs to be a function');

        this.plugins[name] = plugin(this, options);
    }

    /**
     * Retrieve plugin data/instance.
     *
     * Returns whatever the plugin function returned at registration time.
     *
     * @param {string} name - Plugin name
     * @throws Error when no plugin is registered with this name
     * @returns {*}
     */
    getPlugin(name) {
        if (!has(this.plugins, name)) throw new Error(`Plugin "${name}" is not registered`);
        return this.plugins[name];
    }
}

module.exports = Api;
<|endoftext|>


<commit_before>var util = require( "util" ),
    assert = require( "assert" );

var undefined,
    noop = function(){},
    pause = false,
    queue = [],
    test,
    module = {},
    QUnit = {
        api: {}
    };

exports.QUnit = QUnit;

/**
 * Setup the env, run the test and then run the next one
 */
function run() {
    // all tests are done
    if ( queue.length <= 0 ) {
        QUnit.done();
        return;
    }

    test = queue[0];
    module = test.module;

    if ( module.env && module.env.setup && !test.setupDone ) {
        module.env.setup();
        test.setupDone = true;
    }

    // run the test
    if ( !pause ) {
        test.fn.call( QUnit.current_testEnvironment = extend( true, test.env || {}, module.env ) );
    }

    if ( test.async ) {
        QUnit.api.stop();
    } else if ( !pause ) {
        testDone();
    }

    if ( !pause ) {
        run();
    }
}

/**
 * Do teardown and count expected assertions after each test is done
 */
function testDone() {

    if ( !test || !module ) {
        return;
    }

    if ( module.env && module.env.teardown && !test.teardownDone ) {
        module.env.teardown();
        test.teardownDone = true;
    }

    if ( pause ) {
        return;
    }

    // check if expected assertions count is correct
    if ( test.expected !== undefined && test.expected !== test.done ) {
        var errorStack;
        // trigger an error to get an error stack
        try {
            throw new Error( "Assertions amount" );
        } catch( err ) {
            errorStack = err.stack;
        }

        log({
            message: "Expected " + test.expected + " assertions, but " + test.done + " were run",
            errorStack: errorStack
        });
    }

    queue.shift();
}

/**
 * Print stringified object to stdout
 * @param {Object} obj
 */
function log( obj ) {

    obj.message = obj.message || "";
    obj.name = test.name || "";
    obj.module = module.name || "";

    util.print( JSON.stringify(obj) + "\n" );
}

/**
 * Ecma 5 doesn't defines this method, so just hope they also like jQuery's way :)
 * You can pass so much objects as you like, all properties will be copied to the first object
 *
 * @param {Object|Boolean} deep
 * @param {Object}
 * @return {Object}
 */
function extend( deep /*, obj, obj, ...*/ ) {
    // take first argument, if its not a boolean
    var args = arguments,
        i = typeof deep === 'boolean' ? 1 : 0,
        target = args[i];

    for ( ; i < args.length; ++i ) {
        typeof args[i] === "object" && Object.keys( args[i] ).forEach(function( key ) {
            // if deep extending and both of keys are objects
            if ( deep === true && target[key] ) {
                args.callee(deep, target[key], args[i][key]);
            } else {
                target[key] = args[i][key];
            }
        });
    }

    return target;
}

exports.extend = extend;

/**
 * Begin tests execution
 */
QUnit.begin = function() {
    if ( queue.length <= 0 ) {
        throw new Error( "There are no tests defined" );
    }

    this.start();
};

/**
 * Callback for tests complete
 */
QUnit.done = noop;

/**
 * Define current module name and environment
 * @param {String} name
 * @param {Object} env
 */
QUnit.api.module = function( name, env ) {

    env = env || {};

    module = {
        name: name,
        env: env
    };

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 * @param {Boolean} async add test as async one
 */
QUnit.api.test = function( name, expect, fn, async ) {

    if ( typeof name !== "string" ) {
        throw new Error( "Test name should be the first parameter" );
    }

    var type = typeof expect,
        env;
    if ( type === "function" ) {
        fn = expect;
        expect = undefined;
    // expect can extend module env
    } else if ( type === "object" ) {
        env = expect;
        expect = undefined;
    }

    if ( typeof fn !== "function" ) {
        throw new Error( "No test function passed" );
    }

    queue.push({
        name: name,
        module: module,
        fn: fn,
        expected: expect,
        env: env,
        done: 0,
        async: async
    });

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 */
QUnit.api.asyncTest = function( name, expect, fn ) {
    this.test( name, expect, fn, true );
    return this;
};

/**
 * Start tests execution
 */
QUnit.api.start = function() {
    pause = false;
    testDone();
    run();
    return this;
};

/**
 * Stop tests execution
 */
QUnit.api.stop = function() {
    pause = true;
    return this;
};

/**
 * Set expected amount of assertions for current test
 * @param {Number} amount
 */
QUnit.api.expect = function( amount ) {
    test.expected = amount;
    return this;
};

/**
 * A boolean assertion. Passes if the first argument is truthy.
 * @param {Boolean} value
 * @param {String} message
 */
QUnit.api.ok = function( value, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.ok( value );
    } catch (err) {
        r.errorStack = err.stack;
    }

    log(r);

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.equals = QUnit.api.equal = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.equal( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.notEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.notEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ===.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.strictEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.strictEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A deep recursive comparison assertion, working on primitive types, arrays and objects.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.same = QUnit.api.deepEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.deepEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

QUnit.api.raises = function( actual, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.throws( actual, message );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * Original function from QUnit
 * @param {Object} obj
 * @return {String|undefined}
 */
QUnit.objectType = function( obj ) {
    if (typeof obj === "undefined") {
        return "undefined";
    }
    // consider: typeof null === object
    if (obj === null) {
        return "null";
    }

    var type = Object.prototype.toString.call( obj )
        .match(/^\[object\s(.*)\]$/)[1] || '';

    switch (type) {
        case 'Number':
        if (isNaN(obj)) {
            return "nan";
        } else {
            return "number";
        }
        case 'String':
        case 'Boolean':
        case 'Array':
        case 'Date':
        case 'RegExp':
        case 'Function':
            return type.toLowerCase();
    }
    if (typeof obj === "object") {
        return "object";
    }
    return undefined;
}

// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {

    var innerEquiv; // the real equiv function
    var callers = []; // stack to decide between skip/abort functions
    var parents = []; // stack to avoiding loops from circular referencing

    // Call the o related callback with the given arguments.
    function bindCallbacks(o, callbacks, args) {
        var prop = QUnit.objectType(o);
        if (prop) {
            if (QUnit.objectType(callbacks[prop]) === "function") {
                return callbacks[prop].apply(callbacks, args);
            } else {
                return callbacks[prop]; // or undefined
            }
        }
    }

    var callbacks = function () {

        // for string, boolean, number and null
        function useStrictEquality(b, a) {
            if (b instanceof a.constructor || a instanceof b.constructor) {
                // to catch short annotaion VS 'new' annotation of a declaration
                // e.g. var i = 1;
                // var j = new Number(1);
                return a == b;
            } else {
                return a === b;
            }
        }

        return {
            "string": useStrictEquality,
            "boolean": useStrictEquality,
            "number": useStrictEquality,
            "null": useStrictEquality,
            "undefined": useStrictEquality,

            "nan": function (b) {
                return isNaN(b);
            },

            "date": function (b, a) {
                return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
            },

            "regexp": function (b, a) {
                return QUnit.objectType(b) === "regexp" &&
                    a.source === b.source && // the regex itself
                    a.global === b.global && // and its modifers (gmi) ...
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline;
            },

            // - skip when the property is a method of an instance (OOP)
            // - abort otherwise,
            // initial === would have catch identical references anyway
            "function": function () {
                var caller = callers[callers.length - 1];
                return caller !== Object &&
                        typeof caller !== "undefined";
            },

            "array": function (b, a) {
                var i, j, loop;
                var len;

                // b could be an object literal here
                if ( ! (QUnit.objectType(b) === "array")) {
                    return false;
                }

                len = a.length;
                if (len !== b.length) { // safe and faster
                    return false;
                }

                //track reference to avoid circular references
                parents.push(a);
                for (i = 0; i < len; i++) {
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i]){
                            loop = true;//dont rewalk array
                        }
                    }
                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        parents.pop();
                        return false;
                    }
                }
                parents.pop();
                return true;
            },

            "object": function (b, a) {
                var i, j, loop;
                var eq = true; // unless we can proove it
                var aProperties = [], bProperties = []; // collection of strings

                // comparing constructors is more strict than using instanceof
                if ( a.constructor !== b.constructor) {
                    return false;
                }

                // stack constructor before traversing properties
                callers.push(a.constructor);
                //track reference to avoid circular references
                parents.push(a);

                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i])
                            loop = true; //don't go down the same path twice
                    }
                    aProperties.push(i); // collect a's properties

                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        eq = false;
                        break;
                    }
                }

                callers.pop(); // unstack, we are done
                parents.pop();

                for (i in b) {
                    bProperties.push(i); // collect b's properties
                }

                // Ensures identical properties name
                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
            }
        };
    }();

    innerEquiv = function () { // can take multiple arguments
        var args = Array.prototype.slice.apply(arguments);
        if (args.length < 2) {
            return true; // end transition
        }

        return (function (a, b) {
            if (a === b) {
                return true; // catch the most you can
            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
                return false; // don't lose time with error prone cases
            } else {
                return bindCallbacks(a, callbacks, [b, a]);
            }

        // apply transition with (1..n) arguments
        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
    };

    return innerEquiv;

}();

// copy api to QUnit namespace
extend( QUnit, QUnit.api );

global.QUnit = QUnit;

// make qunit api global
extend( global, QUnit.api );
<commit_msg>Hack to prevent extend attempting to overwrite console<commit_after>var util = require( "util" ),
    assert = require( "assert" );

var undefined,
    noop = function(){},
    pause = false,
    queue = [],
    test,
    module = {},
    QUnit = {
        api: {}
    };

exports.QUnit = QUnit;

/**
 * Setup the env, run the test and then run the next one
 */
function run() {
    // all tests are done
    if ( queue.length <= 0 ) {
        QUnit.done();
        return;
    }

    test = queue[0];
    module = test.module;

    if ( module.env && module.env.setup && !test.setupDone ) {
        module.env.setup();
        test.setupDone = true;
    }

    // run the test
    if ( !pause ) {
        test.fn.call( QUnit.current_testEnvironment = extend( true, test.env || {}, module.env ) );
    }

    if ( test.async ) {
        QUnit.api.stop();
    } else if ( !pause ) {
        testDone();
    }

    if ( !pause ) {
        run();
    }
}

/**
 * Do teardown and count expected assertions after each test is done
 */
function testDone() {

    if ( !test || !module ) {
        return;
    }

    if ( module.env && module.env.teardown && !test.teardownDone ) {
        module.env.teardown();
        test.teardownDone = true;
    }

    if ( pause ) {
        return;
    }

    // check if expected assertions count is correct
    if ( test.expected !== undefined && test.expected !== test.done ) {
        var errorStack;
        // trigger an error to get an error stack
        try {
            throw new Error( "Assertions amount" );
        } catch( err ) {
            errorStack = err.stack;
        }

        log({
            message: "Expected " + test.expected + " assertions, but " + test.done + " were run",
            errorStack: errorStack
        });
    }

    queue.shift();
}

/**
 * Print stringified object to stdout
 * @param {Object} obj
 */
function log( obj ) {

    obj.message = obj.message || "";
    obj.name = test.name || "";
    obj.module = module.name || "";

    util.print( JSON.stringify(obj) + "\n" );
}

/**
 * Ecma 5 doesn't defines this method, so just hope they also like jQuery's way :)
 * You can pass so much objects as you like, all properties will be copied to the first object
 *
 * @param {Object|Boolean} deep
 * @param {Object}
 * @return {Object}
 */
function extend( deep /*, obj, obj, ...*/ ) {
    // take first argument, if its not a boolean
    var args = arguments,
        i = typeof deep === 'boolean' ? 1 : 0,
        target = args[i];

    for ( ; i < args.length; ++i ) {
        typeof args[i] === "object" && Object.keys( args[i] ).forEach(function( key ) {
            // if deep extending and both of keys are objects
            if (key == "console") {
              return;
            }
            if ( deep === true && target[key] ) {
                args.callee(deep, target[key], args[i][key]);
            } else {
                target[key] = args[i][key];
            }
        });
    }

    return target;
}

exports.extend = extend;

/**
 * Begin tests execution
 */
QUnit.begin = function() {
    if ( queue.length <= 0 ) {
        throw new Error( "There are no tests defined" );
    }

    this.start();
};

/**
 * Callback for tests complete
 */
QUnit.done = noop;

/**
 * Define current module name and environment
 * @param {String} name
 * @param {Object} env
 */
QUnit.api.module = function( name, env ) {

    env = env || {};

    module = {
        name: name,
        env: env
    };

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 * @param {Boolean} async add test as async one
 */
QUnit.api.test = function( name, expect, fn, async ) {

    if ( typeof name !== "string" ) {
        throw new Error( "Test name should be the first parameter" );
    }

    var type = typeof expect,
        env;
    if ( type === "function" ) {
        fn = expect;
        expect = undefined;
    // expect can extend module env
    } else if ( type === "object" ) {
        env = expect;
        expect = undefined;
    }

    if ( typeof fn !== "function" ) {
        throw new Error( "No test function passed" );
    }

    queue.push({
        name: name,
        module: module,
        fn: fn,
        expected: expect,
        env: env,
        done: 0,
        async: async
    });

    return this;
};

/**
 * Add async test
 * @param {String} name
 * @param {Number} expect optional
 * @param {Function} fn contains the test
 */
QUnit.api.asyncTest = function( name, expect, fn ) {
    this.test( name, expect, fn, true );
    return this;
};

/**
 * Start tests execution
 */
QUnit.api.start = function() {
    pause = false;
    testDone();
    run();
    return this;
};

/**
 * Stop tests execution
 */
QUnit.api.stop = function() {
    pause = true;
    return this;
};

/**
 * Set expected amount of assertions for current test
 * @param {Number} amount
 */
QUnit.api.expect = function( amount ) {
    test.expected = amount;
    return this;
};

/**
 * A boolean assertion. Passes if the first argument is truthy.
 * @param {Boolean} value
 * @param {String} message
 */
QUnit.api.ok = function( value, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.ok( value );
    } catch (err) {
        r.errorStack = err.stack;
    }

    log(r);

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.equals = QUnit.api.equal = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.equal( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ==.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.notEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.notEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A comparison assertion, uses ===.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.strictEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.strictEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * A deep recursive comparison assertion, working on primitive types, arrays and objects.
 * @param {Object} actual
 * @param {Object} expected
 * @param {String} message
 */
QUnit.api.same = QUnit.api.deepEqual = function( actual, expected, message ) {
    test.done++;

    var r = {
            message: message
        };

    try {
        assert.deepEqual( actual, expected );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

QUnit.api.raises = function( actual, message ) {
    test.done++;
    var r = {
            message: message
        };

    try {
        assert.throws( actual, message );
    } catch( err ) {
        r.errorStack = err.stack;
    }

    log( r );

    return this;
};

/**
 * Original function from QUnit
 * @param {Object} obj
 * @return {String|undefined}
 */
QUnit.objectType = function( obj ) {
    if (typeof obj === "undefined") {
        return "undefined";
    }
    // consider: typeof null === object
    if (obj === null) {
        return "null";
    }

    var type = Object.prototype.toString.call( obj )
        .match(/^\[object\s(.*)\]$/)[1] || '';

    switch (type) {
        case 'Number':
        if (isNaN(obj)) {
            return "nan";
        } else {
            return "number";
        }
        case 'String':
        case 'Boolean':
        case 'Array':
        case 'Date':
        case 'RegExp':
        case 'Function':
            return type.toLowerCase();
    }
    if (typeof obj === "object") {
        return "object";
    }
    return undefined;
}

// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = function () {

    var innerEquiv; // the real equiv function
    var callers = []; // stack to decide between skip/abort functions
    var parents = []; // stack to avoiding loops from circular referencing

    // Call the o related callback with the given arguments.
    function bindCallbacks(o, callbacks, args) {
        var prop = QUnit.objectType(o);
        if (prop) {
            if (QUnit.objectType(callbacks[prop]) === "function") {
                return callbacks[prop].apply(callbacks, args);
            } else {
                return callbacks[prop]; // or undefined
            }
        }
    }

    var callbacks = function () {

        // for string, boolean, number and null
        function useStrictEquality(b, a) {
            if (b instanceof a.constructor || a instanceof b.constructor) {
                // to catch short annotaion VS 'new' annotation of a declaration
                // e.g. var i = 1;
                // var j = new Number(1);
                return a == b;
            } else {
                return a === b;
            }
        }

        return {
            "string": useStrictEquality,
            "boolean": useStrictEquality,
            "number": useStrictEquality,
            "null": useStrictEquality,
            "undefined": useStrictEquality,

            "nan": function (b) {
                return isNaN(b);
            },

            "date": function (b, a) {
                return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
            },

            "regexp": function (b, a) {
                return QUnit.objectType(b) === "regexp" &&
                    a.source === b.source && // the regex itself
                    a.global === b.global && // and its modifers (gmi) ...
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline;
            },

            // - skip when the property is a method of an instance (OOP)
            // - abort otherwise,
            // initial === would have catch identical references anyway
            "function": function () {
                var caller = callers[callers.length - 1];
                return caller !== Object &&
                        typeof caller !== "undefined";
            },

            "array": function (b, a) {
                var i, j, loop;
                var len;

                // b could be an object literal here
                if ( ! (QUnit.objectType(b) === "array")) {
                    return false;
                }

                len = a.length;
                if (len !== b.length) { // safe and faster
                    return false;
                }

                //track reference to avoid circular references
                parents.push(a);
                for (i = 0; i < len; i++) {
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i]){
                            loop = true;//dont rewalk array
                        }
                    }
                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        parents.pop();
                        return false;
                    }
                }
                parents.pop();
                return true;
            },

            "object": function (b, a) {
                var i, j, loop;
                var eq = true; // unless we can proove it
                var aProperties = [], bProperties = []; // collection of strings

                // comparing constructors is more strict than using instanceof
                if ( a.constructor !== b.constructor) {
                    return false;
                }

                // stack constructor before traversing properties
                callers.push(a.constructor);
                //track reference to avoid circular references
                parents.push(a);

                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
                    loop = false;
                    for(j=0;j<parents.length;j++){
                        if(parents[j] === a[i])
                            loop = true; //don't go down the same path twice
                    }
                    aProperties.push(i); // collect a's properties

                    if (!loop && ! innerEquiv(a[i], b[i])) {
                        eq = false;
                        break;
                    }
                }

                callers.pop(); // unstack, we are done
                parents.pop();

                for (i in b) {
                    bProperties.push(i); // collect b's properties
                }

                // Ensures identical properties name
                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
            }
        };
    }();

    innerEquiv = function () { // can take multiple arguments
        var args = Array.prototype.slice.apply(arguments);
        if (args.length < 2) {
            return true; // end transition
        }

        return (function (a, b) {
            if (a === b) {
                return true; // catch the most you can
            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
                return false; // don't lose time with error prone cases
            } else {
                return bindCallbacks(a, callbacks, [b, a]);
            }

        // apply transition with (1..n) arguments
        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
    };

    return innerEquiv;

}();

// copy api to QUnit namespace
extend( QUnit, QUnit.api );

global.QUnit = QUnit;

// make qunit api global
extend( global, QUnit.api );
<|endoftext|>