<commit_before>'use strict';

const Promise = require('bluebird');

const fs = Promise.promisifyAll(require('fs'));
const path = require('path');
const url = require('url');
const express = require('express');
const spawn = require('child_process').spawn;
const mkdirp = Promise.promisifyAll(require('mkdirp'));
const semver = require('semver');
const SourceNode = require('source-map').SourceNode;
const SourceMapConsumer = require('source-map').SourceMapConsumer;
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const getReactNativeExternals = require('./getReactNativeExternals');
const waitForSocket = require('socket-retry-connect').waitForSocket;
const fetch = require('./fetch');

const ENTRY_JS = 'global.React = require("react-native");';
const SOURCEMAP_REGEX = /\/\/[#@] sourceMappingURL=([^\s'"]*)/;

function staticImageTransform(context, request, callback) {
  if (/^image!/.test(request)) {
    return callback(null, JSON.stringify({
      uri: request.replace('image!', ''),
      isStatic: true,
    }));
  }
  callback();
}

function makeHotConfig(webpackConfig) {
  webpackConfig.plugins = webpackConfig.plugins || [];
  webpackConfig.plugins.unshift(
    new webpack.BannerPlugin(
      'if (typeof navigator.userAgent === \'undefined\') {\n' +
      '  throw new Error(\'Hot module replacement only works with RCTWebSocketExecutor; use Cmd + D, "Debug in Chrome"\')' +
      '}\n',
      {raw: true, entryOnly: true}
    )
  );
}

class Server {

  /**
   * Create a new server with the following options:
   * @param {String}  hostname
   * @param {Number}  port
   * @param {Number}  packagerPort
   * @param {Number}  webpackPort
   * @param {Boolean} android       Enable Android support
   * @param {Boolean} ios           Enable iOS support
   * @param {String}  androidEntry
   * @param {String}  iosEntry
   * @param {Object}  webpackConfig The webpack config to use for webpack-dev-server
   * @param {Boolean} hot           Enable hot module replacement
   *
   * @constructor
   * @param {Object} options
   */
  constructor(options) {
    this.hostname = options.hostname;
    this.port = options.port;
    this.packagerPort = options.packagerPort;
    this.webpackPort = options.webpackPort;
    this.entries = {
      android: options.androidEntry,
      ios: options.iosEntry,
    };
    this.platforms = options.platforms;
    this.projectRoots = options.projectRoots;
    this.assetRoots = options.assetRoots;
    this.resetCache = !!options.resetCache;
    this.hot = !!options.hot;
    this.webpackConfig = options.webpackConfig;

    // Check for local react-native.
    try {
      require.resolve('react-native');
    } catch (err) {
      throw new Error('Could not find react-native. Try `npm install react-native`.');
    }

    // Construct resource URLs up-front
    this.webpackBaseURL = url.format({
      protocol: 'http',
      hostname: this.hostname,
      port: this.webpackPort,
    });
    this.packagerBaseURL = url.format({
      protocol: 'http',
      hostname: this.hostname,
      port: this.packagerPort,
    });
  }

  start() {
    // Create a stub entry module for the RN packager.
    this.entryDir = path.resolve(__dirname, '../_entry');
    return this._writeEntryFiles().then(() => {
      // Re-throw error if server fails to handle a promise rejection
      process.on('unhandledRejection', reason => {
        throw reason;
      });

      // Make sure to clean up when the process is terminated.
      process.on('exit', () => this.handleProcessExit());
      process.on('SIGINT', () => {
        this.handleProcessExit();
        process.exit(1);
      });

      // Construct a promise waiting for both servers to fully start...
      const readyPromise = this._startWebpackDevServer().then(() =>
        // We need to start this one second to prevent races between the bundlers.
        this._startPackageServer()
      );

      // Setup the express server
      this.server = express();
      this.server.use((req, res, next) => {
        // Wait until packager has started before serving requests
        readyPromise
          .then(() => next())
          .catch(err => next(err));
      });
      this.server.get('/*.bundle', this.handleBundleRequest.bind(this));
      this.server.get('/*.map', this.handleMapRequest.bind(this));
      this.server.use((err, req, res, next) => {
        console.error(err.stack);
        next(err);
      });

      const listenPromise = new Promise(resolve => {
        this.httpServer = this.server.listen(this.port, () => {
          console.log(`Server listening at http://${this.hostname}:${this.port}`);
          resolve();
        });
        // Disable any kind of automatic timeout behavior on incoming connections.
        this.httpServer.timeout = 0;
      });
      return Promise.all([listenPromise, readyPromise]);
    });
  }

  stop() {
    this.handleProcessExit();
    this.httpServer && this.httpServer.close();
    this.webpackServer && this.webpackServer.close();
  }

  handleBundleRequest(req, res, next) {
    const parsedUrl = url.parse(req.url, /* parse query */ true);
    const urlSearch = parsedUrl.search;
    const platform = parsedUrl.query.platform;

    // Forward URL params to RN packager
    const reactCodeURL = this._getReactCodeURL(platform) + urlSearch;
    const appCodeURL = this._getAppCodeURL(platform);

    Promise.props({
      reactCode: fetch(reactCodeURL),
      appCode: fetch(appCodeURL),
    }).then(r =>
      this._createBundleCode(r.reactCode, r.appCode, urlSearch, platform)
    ).then(bundleCode => {
      res.set('Content-Type', 'application/javascript');
      res.send(bundleCode);
    }).catch(err => next(err));
  }

  handleMapRequest(req, res, next) {
    const parsedUrl = url.parse(req.url, /* parse query */ true);
    const urlSearch = parsedUrl.search;
    const platform = parsedUrl.query.platform;

    // Forward URL params to RN packager
    const reactCodeURL = this._getReactCodeURL(platform) + urlSearch;
    const reactMapURL = this._getReactMapURL(platform) + urlSearch;
    const appCodeURL = this._getAppCodeURL(platform);
    const appMapURL = this._getAppMapURL(platform);

    Promise.props({
      reactCode: fetch(reactCodeURL),
      reactMap: fetch(reactMapURL),
      appCode: fetch(appCodeURL),
      appMap: fetch(appMapURL),
    }).then(r =>
      this._createBundleMap(r.reactCode, r.reactMap, r.appCode, r.appMap)
    ).then(bundleMap => {
      res.set('Content-Type', 'application/json');
      res.send(bundleMap);
    }).catch(err => next(err));
  }

  handleProcessExit() {
    // Clean up temp files
    const entryDir = this.entryDir;
    if (fs.existsSync(entryDir)) {
      fs.readdirSync(entryDir).forEach(file => {
        fs.unlinkSync(path.join(entryDir, file));
      });
      fs.rmdirSync(entryDir);
    }

    // Kill the package server
    if (this.packageServer) {
      this.packageServer.kill();
    }
  }

  _getReactCodeURL(platform) {
    return url.resolve(this.packagerBaseURL, `index.${platform}.bundle`);
  }

  _getReactMapURL(platform) {
    return url.resolve(this.packagerBaseURL, `index.${platform}.map`);
  }

  _getAppCodeURL(platform) {
    return url.resolve(this.webpackBaseURL, `${this.entries[platform]}.js`);
  }

  _getAppMapURL(platform) {
    return url.resolve(this.webpackBaseURL, `${this.entries[platform]}.js.map`);
  }

  _createBundleCode(reactCode, appCode, urlSearch, platform) {
    reactCode = reactCode.replace(SOURCEMAP_REGEX, '');
    appCode = appCode.replace(SOURCEMAP_REGEX, '');
    return reactCode + appCode + `//# sourceMappingURL=/${this.entries[platform]}.map${urlSearch}`;
  }

  _createBundleMap(reactCode, reactMap, appCode, appMap) {
    const node = new SourceNode();

    node.add(SourceNode.fromStringWithSourceMap(
      reactCode,
      new SourceMapConsumer(reactMap)
    ));
    node.add(SourceNode.fromStringWithSourceMap(
      appCode,
      new SourceMapConsumer(appMap)
    ));

    return node.join('').toStringWithSourceMap().map.toString();
  }

  _writeEntryFiles() {
    const source = ENTRY_JS + '\n';
    return mkdirp.mkdirpAsync(this.entryDir).then(() => Promise.all([
      fs.writeFileAsync(path.resolve(this.entryDir, 'index.android.js'), source, 'utf8'),
      fs.writeFileAsync(path.resolve(this.entryDir, 'index.ios.js'), source, 'utf8'),
    ]));
  }

  _startPackageServer() {
    /**
     * Starting the server is neither fast nor completely reliable we end up
     * hitting its public api over http periodically so we must wait for it to
     * be actually ready.
     */
    return new Promise((resolve, reject) => {
      // Easier to just shell out to the packager than use the JS API.
      // XXX: Uses the node only invocation so we don't have to deal with bash
      // as well... Fixes issues where server cannot be killed cleanly.
      const cmd = 'node';
      const reactNativeVersion = require('react-native/package.json').version;
      const script =
        semver.lt(reactNativeVersion, '0.14.0')
          ? ['./node_modules/react-native/packager/packager.js']
          : ['./node_modules/react-native/local-cli/cli.js', 'start'];
      const args = script.concat([
        '--root', this.entryDir,
        '--port', this.packagerPort,
      ]).concat(
        this.projectRoots ? ['--projectRoots', this.projectRoots.join(',')] : []
      ).concat(
        this.assetRoots ? ['--assetRoots', this.assetRoots.join(',')] : []
      ).concat(
        this.resetCache
          ? '--reset-cache'
          : []
      );
      const opts = {stdio: 'inherit'};
      this.packageServer = spawn(cmd, args, opts);

      function handleError(err) {
        reject(err);
      }

      this.packageServer.on('error', handleError);

      // waitForSocket retries the port every 250ms. Let's give the
      // React Native server up to 30 seconds to come online. watchman
      // can be slow to ramp up, especially on CI machines.
      waitForSocket({ port: this.packagerPort, tries: 120 }, err => {
        console.log('react-native packager ready...');
        this.packageServer.removeListener('error', handleError);
        if (err) {
          handleError(err);
          return;
        }
        resolve();
      });
    });
  }

  _startWebpackDevServer() {
    const webpackConfig = this.webpackConfig;
    const hot = this.hot;
    return getReactNativeExternals({
      projectRoots: this.projectRoots,
      assetRoots: this.assetRoots,
      platforms: this.platforms,
    }).then(reactNativeExternals => {

      // Coerce externals into an array, without clobbering it
      webpackConfig.externals = Array.isArray(webpackConfig.externals)
        ? webpackConfig.externals
        : [(webpackConfig.externals || {})];

      // Inject react native externals
      webpackConfig.externals.push(reactNativeExternals);

      // Transform static image references
      webpackConfig.externals.push(staticImageTransform);

      // By default webpack uses webpack://[resource-path]?[hash] in the source
      // map which is handled by its dev server. Use absolute path instead so
      // React Native's exception manager can load the source maps.
      webpackConfig.output = webpackConfig.output || {};
      if (!webpackConfig.output.devtoolModuleFilenameTemplate) {
        webpackConfig.output.devtoolModuleFilenameTemplate = '[absolute-resource-path]';
      }

      // Update webpack config for hot mode.
      if (hot) {
        makeHotConfig(webpackConfig);
      }

      // Plug into webpack compilation to extract webpack dependency tree.
      // Any React Native externals from the application source need to be
      // require()'d in the RN packager's entry file. This allows for RN
      // modules that aren't part of the main 'react-native' dependency tree
      // to be included in the generated bundle (e.g. AdSupportIOS).
      const compiler = webpack(webpackConfig);

      const compilerPromise = new Promise(resolve => {
        compiler.plugin('done', () => {
          // Write out the RN packager's entry file
          this._writeEntryFiles().then(resolve);
        });
      });

      this.webpackServer = new WebpackDevServer(compiler, {
        hot: hot,
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
        stats: {colors: true, chunkModules: false},
      });

      const serverPromise = new Promise(resolve => {
        this.webpackServer.listen(this.webpackPort, this.hostname, () => {
          console.log('Webpack dev server listening at ', this.webpackBaseURL);
          resolve();
        });
        // Disable any kind of automatic timeout behavior on incoming connections.
        this.webpackServer.timeout = 0;
      });

      // Ensure that both the server is up and the compiler's entry
      // file has been written for the React Native packager.
      return Promise.all([compilerPromise, serverPromise]);
    });
  }

}

module.exports = Server;
<commit_msg>Harmonize style a bit<commit_after>'use strict';

const Promise = require('bluebird');

const fs = Promise.promisifyAll(require('fs'));
const path = require('path');
const url = require('url');
const express = require('express');
const spawn = require('child_process').spawn;
const mkdirp = Promise.promisifyAll(require('mkdirp'));
const semver = require('semver');
const SourceNode = require('source-map').SourceNode;
const SourceMapConsumer = require('source-map').SourceMapConsumer;
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const getReactNativeExternals = require('./getReactNativeExternals');
const waitForSocket = require('socket-retry-connect').waitForSocket;
const fetch = require('./fetch');

const ENTRY_JS = 'global.React = require("react-native");';
const SOURCEMAP_REGEX = /\/\/[#@] sourceMappingURL=([^\s'"]*)/;

function staticImageTransform(context, request, callback) {
  if (/^image!/.test(request)) {
    return callback(null, JSON.stringify({
      uri: request.replace('image!', ''),
      isStatic: true,
    }));
  }
  callback();
}

function makeHotConfig(webpackConfig) {
  webpackConfig.plugins = webpackConfig.plugins || [];
  webpackConfig.plugins.unshift(
    new webpack.BannerPlugin(
      'if (typeof navigator.userAgent === \'undefined\') {\n' +
      '  throw new Error(\'Hot module replacement only works with RCTWebSocketExecutor; use Cmd + D, "Debug in Chrome"\')' +
      '}\n',
      {raw: true, entryOnly: true}
    )
  );
}

class Server {

  /**
   * Create a new server with the following options:
   * @param {String}  hostname
   * @param {Number}  port
   * @param {Number}  packagerPort
   * @param {Number}  webpackPort
   * @param {Boolean} android       Enable Android support
   * @param {Boolean} ios           Enable iOS support
   * @param {String}  androidEntry
   * @param {String}  iosEntry
   * @param {Object}  webpackConfig The webpack config to use for webpack-dev-server
   * @param {Boolean} hot           Enable hot module replacement
   *
   * @constructor
   * @param {Object} options
   */
  constructor(options) {
    this.hostname = options.hostname;
    this.port = options.port;
    this.packagerPort = options.packagerPort;
    this.webpackPort = options.webpackPort;
    this.entries = {
      android: options.androidEntry,
      ios: options.iosEntry,
    };
    this.platforms = options.platforms;
    this.projectRoots = options.projectRoots;
    this.assetRoots = options.assetRoots;
    this.resetCache = !!options.resetCache;
    this.hot = !!options.hot;
    this.webpackConfig = options.webpackConfig;

    // Check for local react-native.
    try {
      require.resolve('react-native');
    } catch (err) {
      throw new Error('Could not find react-native. Try `npm install react-native`.');
    }

    // Construct resource URLs up-front
    this.webpackBaseURL = url.format({
      protocol: 'http',
      hostname: this.hostname,
      port: this.webpackPort,
    });
    this.packagerBaseURL = url.format({
      protocol: 'http',
      hostname: this.hostname,
      port: this.packagerPort,
    });
  }

  start() {
    // Create a stub entry module for the RN packager.
    this.entryDir = path.resolve(__dirname, '../_entry');
    return this._writeEntryFiles().then(() => {
      // Re-throw error if server fails to handle a promise rejection
      process.on('unhandledRejection', reason => {
        throw reason;
      });

      // Make sure to clean up when the process is terminated.
      process.on('exit', () => this.handleProcessExit());
      process.on('SIGINT', () => {
        this.handleProcessExit();
        process.exit(1);
      });

      // Construct a promise waiting for both servers to fully start...
      const readyPromise = this._startWebpackDevServer().then(() =>
        // We need to start this one second to prevent races between the bundlers.
        this._startPackageServer()
      );

      // Setup the express server
      this.server = express();
      this.server.use((req, res, next) => {
        // Wait until packager has started before serving requests
        readyPromise
          .then(() => next())
          .catch(err => next(err));
      });
      this.server.get('/*.bundle', this.handleBundleRequest.bind(this));
      this.server.get('/*.map', this.handleMapRequest.bind(this));
      this.server.use((err, req, res, next) => {
        console.error(err.stack);
        next(err);
      });

      const listenPromise = new Promise(resolve => {
        this.httpServer = this.server.listen(this.port, () => {
          console.log(`Server listening at http://${this.hostname}:${this.port}`);
          resolve();
        });
        // Disable any kind of automatic timeout behavior on incoming connections.
        this.httpServer.timeout = 0;
      });
      return Promise.all([listenPromise, readyPromise]);
    });
  }

  stop() {
    this.handleProcessExit();
    this.httpServer && this.httpServer.close();
    this.webpackServer && this.webpackServer.close();
  }

  handleBundleRequest(req, res, next) {
    const parsedUrl = url.parse(req.url, /* parse query */ true);
    const urlSearch = parsedUrl.search;
    const platform = parsedUrl.query.platform;

    // Forward URL params to RN packager
    const reactCodeURL = this._getReactCodeURL(platform) + urlSearch;
    const appCodeURL = this._getAppCodeURL(platform);

    Promise.props({
      reactCode: fetch(reactCodeURL),
      appCode: fetch(appCodeURL),
    }).then(r =>
      this._createBundleCode(r.reactCode, r.appCode, urlSearch, platform)
    ).then(bundleCode => {
      res.set('Content-Type', 'application/javascript');
      res.send(bundleCode);
    }).catch(err => next(err));
  }

  handleMapRequest(req, res, next) {
    const parsedUrl = url.parse(req.url, /* parse query */ true);
    const urlSearch = parsedUrl.search;
    const platform = parsedUrl.query.platform;

    // Forward URL params to RN packager
    const reactCodeURL = this._getReactCodeURL(platform) + urlSearch;
    const reactMapURL = this._getReactMapURL(platform) + urlSearch;
    const appCodeURL = this._getAppCodeURL(platform);
    const appMapURL = this._getAppMapURL(platform);

    Promise.props({
      reactCode: fetch(reactCodeURL),
      reactMap: fetch(reactMapURL),
      appCode: fetch(appCodeURL),
      appMap: fetch(appMapURL),
    }).then(r =>
      this._createBundleMap(r.reactCode, r.reactMap, r.appCode, r.appMap)
    ).then(bundleMap => {
      res.set('Content-Type', 'application/json');
      res.send(bundleMap);
    }).catch(err => next(err));
  }

  handleProcessExit() {
    // Clean up temp files
    const entryDir = this.entryDir;
    if (fs.existsSync(entryDir)) {
      fs.readdirSync(entryDir).forEach(file => {
        fs.unlinkSync(path.join(entryDir, file));
      });
      fs.rmdirSync(entryDir);
    }

    // Kill the package server
    if (this.packageServer) {
      this.packageServer.kill();
    }
  }

  _getReactCodeURL(platform) {
    return url.resolve(this.packagerBaseURL, `index.${platform}.bundle`);
  }

  _getReactMapURL(platform) {
    return url.resolve(this.packagerBaseURL, `index.${platform}.map`);
  }

  _getAppCodeURL(platform) {
    return url.resolve(this.webpackBaseURL, `${this.entries[platform]}.js`);
  }

  _getAppMapURL(platform) {
    return url.resolve(this.webpackBaseURL, `${this.entries[platform]}.js.map`);
  }

  _createBundleCode(reactCode, appCode, urlSearch, platform) {
    reactCode = reactCode.replace(SOURCEMAP_REGEX, '');
    appCode = appCode.replace(SOURCEMAP_REGEX, '');
    return reactCode + appCode + `//# sourceMappingURL=/${this.entries[platform]}.map${urlSearch}`;
  }

  _createBundleMap(reactCode, reactMap, appCode, appMap) {
    const node = new SourceNode();

    node.add(SourceNode.fromStringWithSourceMap(
      reactCode,
      new SourceMapConsumer(reactMap)
    ));
    node.add(SourceNode.fromStringWithSourceMap(
      appCode,
      new SourceMapConsumer(appMap)
    ));

    return node.join('').toStringWithSourceMap().map.toString();
  }

  _writeEntryFiles() {
    const source = ENTRY_JS + '\n';
    return mkdirp.mkdirpAsync(this.entryDir).then(() => Promise.all([
      fs.writeFileAsync(path.resolve(this.entryDir, 'index.android.js'), source, 'utf8'),
      fs.writeFileAsync(path.resolve(this.entryDir, 'index.ios.js'), source, 'utf8'),
    ]));
  }

  _startPackageServer() {
    /**
     * Starting the server is neither fast nor completely reliable we end up
     * hitting its public api over http periodically so we must wait for it to
     * be actually ready.
     */
    return new Promise((resolve, reject) => {
      // Easier to just shell out to the packager than use the JS API.
      // XXX: Uses the node only invocation so we don't have to deal with bash
      // as well... Fixes issues where server cannot be killed cleanly.
      const cmd = 'node';
      const reactNativeVersion = require('react-native/package.json').version;
      const script =
        semver.lt(reactNativeVersion, '0.14.0')
          ? ['./node_modules/react-native/packager/packager.js']
          : ['./node_modules/react-native/local-cli/cli.js', 'start'];
      const args = script.concat([
        '--root', this.entryDir,
        '--port', this.packagerPort,
      ]).concat(
        this.projectRoots ? ['--projectRoots', this.projectRoots.join(',')] : []
      ).concat(
        this.assetRoots ? ['--assetRoots', this.assetRoots.join(',')] : []
      ).concat(
        this.resetCache ? '--reset-cache' : []
      );
      const opts = {stdio: 'inherit'};
      this.packageServer = spawn(cmd, args, opts);

      function handleError(err) {
        reject(err);
      }

      this.packageServer.on('error', handleError);

      // waitForSocket retries the port every 250ms. Let's give the
      // React Native server up to 30 seconds to come online. watchman
      // can be slow to ramp up, especially on CI machines.
      waitForSocket({ port: this.packagerPort, tries: 120 }, err => {
        console.log('react-native packager ready...');
        this.packageServer.removeListener('error', handleError);
        if (err) {
          handleError(err);
          return;
        }
        resolve();
      });
    });
  }

  _startWebpackDevServer() {
    const webpackConfig = this.webpackConfig;
    const hot = this.hot;
    return getReactNativeExternals({
      projectRoots: this.projectRoots,
      assetRoots: this.assetRoots,
      platforms: this.platforms,
    }).then(reactNativeExternals => {

      // Coerce externals into an array, without clobbering it
      webpackConfig.externals = Array.isArray(webpackConfig.externals)
        ? webpackConfig.externals
        : [(webpackConfig.externals || {})];

      // Inject react native externals
      webpackConfig.externals.push(reactNativeExternals);

      // Transform static image references
      webpackConfig.externals.push(staticImageTransform);

      // By default webpack uses webpack://[resource-path]?[hash] in the source
      // map which is handled by its dev server. Use absolute path instead so
      // React Native's exception manager can load the source maps.
      webpackConfig.output = webpackConfig.output || {};
      if (!webpackConfig.output.devtoolModuleFilenameTemplate) {
        webpackConfig.output.devtoolModuleFilenameTemplate = '[absolute-resource-path]';
      }

      // Update webpack config for hot mode.
      if (hot) {
        makeHotConfig(webpackConfig);
      }

      // Plug into webpack compilation to extract webpack dependency tree.
      // Any React Native externals from the application source need to be
      // require()'d in the RN packager's entry file. This allows for RN
      // modules that aren't part of the main 'react-native' dependency tree
      // to be included in the generated bundle (e.g. AdSupportIOS).
      const compiler = webpack(webpackConfig);

      const compilerPromise = new Promise(resolve => {
        compiler.plugin('done', () => {
          // Write out the RN packager's entry file
          this._writeEntryFiles().then(resolve);
        });
      });

      this.webpackServer = new WebpackDevServer(compiler, {
        hot: hot,
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
        stats: {colors: true, chunkModules: false},
      });

      const serverPromise = new Promise(resolve => {
        this.webpackServer.listen(this.webpackPort, this.hostname, () => {
          console.log('Webpack dev server listening at ', this.webpackBaseURL);
          resolve();
        });
        // Disable any kind of automatic timeout behavior on incoming connections.
        this.webpackServer.timeout = 0;
      });

      // Ensure that both the server is up and the compiler's entry
      // file has been written for the React Native packager.
      return Promise.all([compilerPromise, serverPromise]);
    });
  }

}

module.exports = Server;
<|endoftext|>


<commit_before>var assert = require('assert')
var resolve = require('path').resolve
var url = require('url')

var log = require('npmlog')
var readPackageJson = require('read-package-json')

var mapToRegistry = require('./utils/map-to-registry.js')
var npa = require('npm-package-arg')
var npm = require('./npm.js')

module.exports = access

access.usage = 'npm access public [<package>]' +
               '\nnpm access restricted [<package>]' +
               '\nnpm access add <read-only|read-write> <entity> [<package>]' +
               '\nnpm access rm <entity> [<package>]' +
               '\nnpm access ls [<package>]' +
               '\nnpm access edit [<package>]'

access.completion = function (opts, cb) {
  var argv = opts.conf.argv.remain
  if (argv.length === 2) {
    return cb(null, ['public', 'restricted', 'add', 'rm', 'ls', 'edit'])
  }

  switch (argv[2]) {
    case 'public':
    case 'restricted':
    case 'ls':
    case 'edit':
      return cb(new Error('unimplemented: packages you can change'))
    case 'add':
      if (argv.length === 3) return cb(null, ['read-only', 'read-write'])

      return cb(new Error('unimplemented: entities and packages'))
    case 'rm':
      return cb(new Error('unimplemented: entities and packages'))
    default:
      return cb(new Error(argv[2] + ' not recognized'))
  }
}

function access (args, cb) {
  var cmd = args.shift()
  switch (cmd) {
    case 'public': case 'restricted': return changeAccess(args, cmd, cb)
    case 'add': case 'set': return add(args, cb)
    case 'rm': case 'del': case 'clear': return rm(args, cb)
    case 'list': case 'sl': case 'ls': return ls(args, cb)
    case 'edit': case 'ed': return edit(args, cb)
    default: return cb('Usage:\n' + access.usage)
  }
}

function changeAccess (args, level, cb) {
  assert(Array.isArray(args), 'changeAccess requires args be an array')
  assert(
    ['public', 'restricted'].indexOf(level) !== -1,
    "access level must be either 'public' or 'restricted'"
  )
  assert(typeof cb === 'function', 'changeAccess requires a callback')

  var p = (args.shift() || '').trim()
  if (!p) return getCurrentPackage(level, cb)
  changeAccess_(p, level, cb)
}

function getCurrentPackage (level, cb) {
  var here = resolve(npm.prefix, 'package.json')
  log.verbose('setPackageLevel', 'here', here)

  readPackageJson(here, function (er, data) {
    if (er) return cb(er)

    if (!data.name) {
      return cb(new Error('Package must be named'))
    }

    changeAccess_(data.name, level, cb)
  })
}

function changeAccess_ (name, level, cb) {
  log.verbose('changeAccess', 'name', name, 'level', level)
  mapToRegistry(name, npm.config, function (er, uri, auth, base) {
    if (er) return cb(er)

    var data = npa(name)
    if (!data.scope) {
      var msg = "Sorry, you can't change the access level of unscoped packages."
      log.error('access', msg)
      return cb(new Error(msg))
    }

    // name must be scoped, so escape separator
    name = name.replace('/', '%2f')
    // FIXME: mapToRegistry still isn't generic enough SIGH
    uri = url.resolve(base, '-/package/' + name + '/access')
    var params = {
      level: level,
      auth: auth
    }

    npm.registry.access(uri, params, cb)
  })
}

function add (args, cb) {
  return cb(new Error("npm access add isn't implemented yet!"))
}

function rm (args, cb) {
  return cb(new Error("npm access rm isn't implemented yet!"))
}

function ls (args, cb) {
  return cb(new Error("npm access ls isn't implemented yet!"))
}

function edit (args, cb) {
  return cb(new Error("npm access edit isn't implemented yet!"))
}
<commit_msg>access: updated to support teams and orgs features<commit_after>'use strict'

var resolve = require('path').resolve

var readPackageJson = require('read-package-json')
var mapToRegistry = require('./utils/map-to-registry.js')
var npm = require('./npm.js')

var whoami = require('./whoami')

module.exports = access

access.usage =
  'npm access public [<package>]\n' +
  'npm access restricted [<package>]\n' +
  'npm access grant <read-only|read-write> <scope:team> [<package>]\n' +
  'npm access revoke <scope:team> [<package>]\n' +
  'npm access ls-packages [<user>|<scope>|<scope:team>]\n' +
  'npm access ls-collaborators [<package> [<user>]]\n' +
  'npm access edit [<package>]'

access.subcommands = ['public', 'restricted', 'grant', 'revoke',
                      'ls-packages', 'ls-collaborators', 'edit']

access.completion = function (opts, cb) {
  var argv = opts.conf.argv.remain
  if (argv.length === 2) {
    return cb(null, access.subcommands)
  }

  switch (argv[2]) {
    case 'grant':
      if (argv.length === 3) {
        return cb(null, ['read-only', 'read-write'])
      } else {
        return cb(null, [])
      }
      break
    case 'public':
    case 'restricted':
    case 'ls-packages':
    case 'ls-collaborators':
    case 'edit':
      return cb(null, [])
    case 'revoke':
      return cb(null, [])
    default:
      return cb(new Error(argv[2] + ' not recognized'))
  }
}

function access (args, cb) {
  var cmd = args.shift()
  var params
  return parseParams(cmd, args, function (err, p) {
    if (err) { return cb(err) }
    params = p
    return mapToRegistry(params.package, npm.config, invokeCmd)
  })

  function invokeCmd (err, uri, auth, base) {
    if (err) { return cb(err) }
    params.auth = auth
    try {
      return npm.registry.access(cmd, uri, params, function (err, data) {
        !err && data && console.log(JSON.stringify(data, undefined, 2))
        cb(err, data)
      })
    } catch (e) {
      cb(e.message + '\n\nUsage:\n' + access.usage)
    }
  }
}

function parseParams (cmd, args, cb) {
  var params = {}
  if (cmd === 'grant') {
    params.permissions = args.shift()
  }
  if (['grant', 'revoke', 'ls-packages'].indexOf(cmd) !== -1) {
    var entity = (args.shift() || '').split(':')
    params.scope = entity[0]
    params.team = entity[1]
  }
  getPackage(args.shift(), function (err, pkg) {
    if (err) { return cb(err) }
    params.package = pkg

    if (!params.scope && cmd === 'ls-packages') {
      whoami([], true, function (err, scope) {
        params.scope = scope
        cb(err, params)
      })
    } else {
      if (cmd === 'ls-collaborators') {
        params.user = args.shift()
      }
      cb(null, params)
    }
  })
}

function getPackage (name, cb) {
  if (name && name.trim()) {
    cb(null, name.trim())
  } else {
    readPackageJson(
      resolve(npm.prefix, 'package.json'),
      function (err, data) { cb(err, data.name) })
  }
}
<|endoftext|>


<commit_before>;(function(){

var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz'
var alias = { o:0, i:1, l:1, s:5 }

var lookup = function() {
    var table = {}
    for (var i = 0; i < alphabet.length; i++) {
        table[alphabet[i]] = i
    }
    for (var key in alias) {
        if (!alias.hasOwnProperty(key)) continue
        table[key] = table['' + alias[key]]
    }
    lookup = function() { return table }
    return table
}

function Encoder() {
    var skip = 0 // how many bits we will skip from the first byte
    var bits = 0 // 5 high bits, carry from one byte to the next

    this.output = ''

    this.readByte = function(byte) {
        // coerce the byte to an int
        if (typeof byte == 'string') byte = byte.charCodeAt(0)
        //console.log(byte.toBinary(8) + ' > s ' + skip)

        if (skip < 0) { // we have a carry from the previous byte
            //console.log('carry ' + bits.toBinary(8))
            bits |= (byte >> (-skip))
        } else { // no carry
            bits = (byte << skip) & 248
        }

        if (skip > 3) {
            // not enough data to produce a character, get us another one
            skip -= 8
            return 1
        }

        if (skip < 4) {
            // produce a character
            //console.log('         > ' + (bits >> 3).toBinary(5))
            this.output += alphabet[bits >> 3]
            skip += 5
        }

        return 0
    }

    this.finish = function(check) {
        var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '')
        this.output = ''
        return output
    }
}

Encoder.prototype.update = function(input, flush) {
    for (var i = 0; i < input.length; ) {
        i += this.readByte(input[i])
    }
    var output = this.output
    this.output = ''
    if (flush) {
      output += this.finish()
    }
    return output
}

function Decoder() {
    var skip = 0 // how many bits we have from the previous character
    var byte = 0 // current byte we're producing

    this.output = ''

    // consume a character from the stream
    this.readChar = function(char) {
        //console.log('Read: ' + char + ' \tPos: ' + skip + ' \tCarry: ' + byte)
        if (typeof char != 'string'){
            if (typeof char == 'number') {
                char = String.fromCharCode(char)
            }
        }
        char = char.toLowerCase()
        var val = lookup()[char]
        if (typeof val == 'undefined') {
            // character does not exist in our lookup table
            return
            throw Error('Could not find character "' + char + '" in lookup table.')
        }
        //console.log(' Val: ' + val + ' 0b' + val.toBinary())
        val <<= 3 // move to the high bits

        byte |= val >>> skip
        //console.log(' Update: ' + byte.toBinary())
        skip += 5

        if (skip >= 8) {
            //console.log(' Produce: ' + byte.toBinary())

            this.output += String.fromCharCode(byte)
            skip -= 8
            if (skip > 0) byte = (val << (5 - skip)) & 255
            else byte = 0
            //console.log(' Left: ' + byte.toBinary())
        }

    }

    this.finish = function(check) {
        var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '')
        this.output = ''
        return output
    }
}

Decoder.prototype.update = function(input, flush) {
    for (var i = 0; i < input.length; i++) {
        this.readChar(input[i])
    }
    var output = this.output
    this.output = ''
    if (flush) {
      output += this.finish()
    }
    return output
}

function decode(input) {
    decoder = new Decoder()
    var output = decoder.update(input, true)
    return output
}

function encode(input) {
  encoder = new Encoder()
  var output = encoder.update(input, true)
  return output
}

var crypto, fs
function sha1(input, cb) {
    if (typeof crypto == 'undefined') crypto = require('crypto')
    var hash = crypto.createHash('sha1')
    hash.digest = (function(digest) {
        return function() {
            return encode(digest.call(this, 'binary'))
        }
    })(hash.digest)
    if (cb) { // streaming
        if (typeof input == 'string' || Buffer.isBuffer(input)) {
            try {
                return cb(null, sha1(input))
            } catch (err) {
                return cb(err, null)
            }
        }
        if (!typeof input.on == 'function') return cb({ message: "Not a stream!" })
        input.on('data', function(chunk) { hash.update(chunk) })
        input.on('end', function() { cb(null, hash.digest()) })
        return
    }

    // non-streaming
    if (input) {
        return hash.update(input).digest()
    }
    return hash
}
sha1.file = function(filename, cb) {
    if (filename == '-') {
        process.stdin.resume()
        return sha1(process.stdin, cb)
    }
    if (typeof fs == 'undefined') fs = require('fs')
    return fs.stat(filename, function(err, stats) {
        if (err) return cb(err, null)
        if (stats.isDirectory()) return cb({ dir: true, message: "Is a directory" })
        return sha1(require('fs').createReadStream(filename), cb)
    })
}

module.exports = {
    Decoder: Decoder,
    Encoder: Encoder,
    encode: encode,
    decode: decode,
    sha1: sha1
}

})();
<commit_msg>Browser JS support<commit_after>;(function(){

var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz'
var alias = { o:0, i:1, l:1, s:5 }

var lookup = function() {
    var table = {}
    for (var i = 0; i < alphabet.length; i++) {
        table[alphabet[i]] = i
    }
    for (var key in alias) {
        if (!alias.hasOwnProperty(key)) continue
        table[key] = table['' + alias[key]]
    }
    lookup = function() { return table }
    return table
}

function Encoder() {
    var skip = 0 // how many bits we will skip from the first byte
    var bits = 0 // 5 high bits, carry from one byte to the next

    this.output = ''

    this.readByte = function(byte) {
        // coerce the byte to an int
        if (typeof byte == 'string') byte = byte.charCodeAt(0)
        //console.log(byte.toBinary(8) + ' > s ' + skip)

        if (skip < 0) { // we have a carry from the previous byte
            //console.log('carry ' + bits.toBinary(8))
            bits |= (byte >> (-skip))
        } else { // no carry
            bits = (byte << skip) & 248
        }

        if (skip > 3) {
            // not enough data to produce a character, get us another one
            skip -= 8
            return 1
        }

        if (skip < 4) {
            // produce a character
            //console.log('         > ' + (bits >> 3).toBinary(5))
            this.output += alphabet[bits >> 3]
            skip += 5
        }

        return 0
    }

    this.finish = function(check) {
        var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '')
        this.output = ''
        return output
    }
}

Encoder.prototype.update = function(input, flush) {
    for (var i = 0; i < input.length; ) {
        i += this.readByte(input[i])
    }
    var output = this.output
    this.output = ''
    if (flush) {
      output += this.finish()
    }
    return output
}

function Decoder() {
    var skip = 0 // how many bits we have from the previous character
    var byte = 0 // current byte we're producing

    this.output = ''

    // consume a character from the stream
    this.readChar = function(char) {
        //console.log('Read: ' + char + ' \tPos: ' + skip + ' \tCarry: ' + byte)
        if (typeof char != 'string'){
            if (typeof char == 'number') {
                char = String.fromCharCode(char)
            }
        }
        char = char.toLowerCase()
        var val = lookup()[char]
        if (typeof val == 'undefined') {
            // character does not exist in our lookup table
            return
            throw Error('Could not find character "' + char + '" in lookup table.')
        }
        //console.log(' Val: ' + val + ' 0b' + val.toBinary())
        val <<= 3 // move to the high bits

        byte |= val >>> skip
        //console.log(' Update: ' + byte.toBinary())
        skip += 5

        if (skip >= 8) {
            //console.log(' Produce: ' + byte.toBinary())

            this.output += String.fromCharCode(byte)
            skip -= 8
            if (skip > 0) byte = (val << (5 - skip)) & 255
            else byte = 0
            //console.log(' Left: ' + byte.toBinary())
        }

    }

    this.finish = function(check) {
        var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '')
        this.output = ''
        return output
    }
}

Decoder.prototype.update = function(input, flush) {
    for (var i = 0; i < input.length; i++) {
        this.readChar(input[i])
    }
    var output = this.output
    this.output = ''
    if (flush) {
      output += this.finish()
    }
    return output
}

function decode(input) {
    decoder = new Decoder()
    var output = decoder.update(input, true)
    return output
}

function encode(input) {
  encoder = new Encoder()
  var output = encoder.update(input, true)
  return output
}

var crypto, fs
function sha1(input, cb) {
    if (typeof crypto == 'undefined') crypto = require('crypto')
    var hash = crypto.createHash('sha1')
    hash.digest = (function(digest) {
        return function() {
            return encode(digest.call(this, 'binary'))
        }
    })(hash.digest)
    if (cb) { // streaming
        if (typeof input == 'string' || Buffer.isBuffer(input)) {
            try {
                return cb(null, sha1(input))
            } catch (err) {
                return cb(err, null)
            }
        }
        if (!typeof input.on == 'function') return cb({ message: "Not a stream!" })
        input.on('data', function(chunk) { hash.update(chunk) })
        input.on('end', function() { cb(null, hash.digest()) })
        return
    }

    // non-streaming
    if (input) {
        return hash.update(input).digest()
    }
    return hash
}
sha1.file = function(filename, cb) {
    if (filename == '-') {
        process.stdin.resume()
        return sha1(process.stdin, cb)
    }
    if (typeof fs == 'undefined') fs = require('fs')
    return fs.stat(filename, function(err, stats) {
        if (err) return cb(err, null)
        if (stats.isDirectory()) return cb({ dir: true, message: "Is a directory" })
        return sha1(require('fs').createReadStream(filename), cb)
    })
}

base32 = {
    Decoder: Decoder,
    Encoder: Encoder,
    encode: encode,
    decode: decode,
    sha1: sha1
}

if (typeof window !== 'undefined') {
  // we're in a browser - OMG!
  window.base32 = base32
} else {
  module.exports = base32
}
})();
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var data;
var buf = module.exports = new Buffer(8);
var colPalette = require('./color-map');

var rs = fs.readFile(__dirname + '/../img/palette-bitmap.bmp', function(err, data){

  if (err) return console.log('error buffering bitmap', err);
    colPalette.darken(data);

    fs.writeFile(__dirname + '/../img/darkenImage.bmp', data, function() {
        if (err) return console.log(err);

        CPalette.invert(data);

            fs.writeFile(__dirname + '/../img/invertImage.bmp', data, function() {
               if (err) return console.log(err);
        });
    });
});

exports.data = data;

<commit_msg>Update buffer.js<commit_after>'use strict';

var fs = require('fs');
var data;
var buf = module.exports = new Buffer(8);
var colPalette = require('./color-map');

var rs = fs.readFile(__dirname + '/../img/palette-bitmap.bmp', function(err, data){

  if (err) return console.log('error buffering bitmap', err);
    colPalette.darken(data);

    fs.writeFile(__dirname + '/../img/darkenImage.bmp', data, function() {

        CPalette.invert(data);

            fs.writeFile(__dirname + '/../img/invertImage.bmp', data, function() {
               if (err) return console.log(err);
        });
    });
});

exports.data = data;

<|endoftext|>


<commit_before>var Util, DOM, CloudFunc, join;

(function(scope, Util, DOM, CloudFunc, join) {
    'use strict';

    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);

    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key,
            Debug,
            log                     = function(str) {
                if (Debug)
                    console.log(str);
            },

            Listeners,
            Files                   = DOM.Files,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage,
            type                    = Util.type;

        this.log                    = log,
        this.PREFIX                 = '',
        this.MousePosition          = {};
        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.HOST                   = location.origin ||
                                      location.protocol + '//' + location.host;

        log.enable     = function() {
            Debug = true;
        };

        log.disable    = function() {
            Debug = false;
        };

        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param params - {
         *          paramLink - ссылка
         *          needRefresh - необходимость обязательной загрузки данных с сервера
         *          panel
         *      }
         * @param callback
         */
        this.loadDir                = function(params, callback) {
            var link, imgPosition, panelChanged, pathParams,
                isRefresh, panel, history,
                p               = params;

            if (params) {
                pathParams      = p.path;
                isRefresh       = p.isRefresh;
                panel           = p.panel;
                history         = p.history;
            }

            if (pathParams)
                link = pathParams;

            if (!params.noCurrent)
                if (panel && panel !== Info.panel) {
                    DOM.changePanel();
                    panelChanged = true;
                }

            if (panelChanged || isRefresh)
                imgPosition = 'top';

            Images.show.load(imgPosition);

            /* загружаем содержимое каталога */
            ajaxLoad(link, {
                refresh     : isRefresh,
                history     : history,
                noCurrent   : params.noCurrent
            }, panel, callback);
        };

        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(params) {
            var name, path, func, doBefore,
                funcName, isContain;

            if (params) {
                name        = params.name,
                path        = params.path,
                func        = params.func,
                funcName    = params.funcName,
                doBefore    = params.dobefore;

                if (path && !name) {
                    name = getStrBigFirst(path);
                    name = name.replace(/.js$/, '');
                }

                isContain   = /\.js/.test(path);

                if (!isContain)
                    path += '.js';

                if (!CloudCmd[name]) {
                    CloudCmd[name] = function() {
                        var pathFull    = CloudCmd.LIBDIRCLIENT + path,
                            args        = arguments;

                        Util.exec(doBefore);

                        return DOM.load.js(pathFull, func ||
                            function(error) {
                                var Proto = CloudCmd[name];

                                if (!error && type.function(Proto))
                                    CloudCmd[name] = applyConstructor(Proto, args);
                            });
                    };

                    CloudCmd[name][funcName] = CloudCmd[name];
                }
            }
        }

        /*
         * apply arguemnts to constructor
         *
         * @param constructor
         * @param args
         */
        function applyConstructor(constructor, args) {
            var F = function () {
                return constructor.apply(this, args);
            };

            F.prototype = constructor.prototype;
            return new F();
        }

        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function(prefix) {
            var func        = function() {
                    Util.exec.series([
                        initModules,
                        baseInit,
                        function() {
                            CloudCmd.route(location.hash);
                        }
                    ]);
                },

                funcBefore  = function(callback) {
                    var src     = CloudCmd.LIBDIRCLIENT + 'polyfill.js';

                    DOM.loadJquery(function() {
                        DOM.load.js(src, callback);
                    });
                };

            CloudCmd.PREFIX = prefix;
            CloudCmd.LIBDIR         = prefix + '/lib/';
            CloudCmd.LIBDIRCLIENT   = prefix + '/lib/client/';

            Util.exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
        };

        this.join                   = function(urls) {
            var prefix  = CloudCmd.PREFIX;

            Util.check(arguments, ['urls']);

            urls = urls.map(function(url) {
                return url.replace(prefix, '');
            });

            return prefix + join(urls);
        };

        this.route                   = function(path) {
            var module, file, current, msg,
                query   = path.split('/');

            if (path) {
                module      = query[0];
                module      = module.slice(1).join('');
                module      = getStrBigFirst(module);

                file        = query[1];
                current     = DOM.getCurrentByName(file);

                if (file && !current) {
                    msg = CloudFunc.formatMsg('set current file', file, 'error');
                    CloudCmd.log(msg);
                } else {
                    DOM.setCurrentFile(current);
                    CloudCmd.execFromModule(module, 'show');
                }
            }
        };

        function initModules(callback) {
            Util.exec.if(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path    : 'key.js',
                    func    : callback
                });
            });

            Files.get('modules', function(error, modules) {
                var storageObj, mod, path,
                    STORAGE     = 'storage',
                    showLoad    = Images.show.load,

                    doBefore    = {
                        'edit'                  : showLoad,
                        'menu'                  : showLoad,
                        'storage/_filepicker'   : showLoad
                    },

                    load = function(name, path, func) {
                        var isTmpl = path === 'template';

                        loadModule({
                            name        : name,
                            funcName    : isTmpl ? 'get' : 'show',
                            path        : path,
                            dobefore    : func
                        });
                    };

                if (!modules)
                    modules = [];

                modules.forEach(function(module) {
                    var isStr = type.string(module);

                    if (isStr)
                        load(null, module, doBefore[module]);
                });

                storageObj = Util.findObjByNameInArr(modules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);

                mod.forEach(function(name) {
                    path = STORAGE + '/_' + name.toLowerCase();

                    load(name, path, doBefore[path]);
                });

                callback();
            });
        }

        function baseInit(callback) {
            var dirPath = '',
                files   = DOM.getFiles(),
                LEFT    = CloudFunc.PANEL_LEFT,
                RIGHT   = CloudFunc.PANEL_RIGHT;

            /* выделяем строку с первым файлом */
            if (files)
                DOM.setCurrentFile(files[0]);

            dirPath     = DOM.getCurrentDirPath(),
            Listeners   = CloudCmd.Listeners;
            Listeners.init();

            Listeners.setOnPanel(LEFT);
            Listeners.setOnPanel(RIGHT);

            Listeners.initKeysPanel();

            /* Устанавливаем кэш корневого каталога */
            dirPath     = CloudFunc.rmLastSlash(dirPath);

            Storage.get(dirPath, function(error, data) {
                if (!data) {
                    data    = getJSONfromFileTable();
                    Storage.set(dirPath, data);
                }
            });

            callback();
        }

        this.execFromModule         = function(moduleName, funcName, params) {
            var obj     = CloudCmd[moduleName],
                isObj   = Util.type.object(obj);

            Util.exec.if(isObj,
                function() {
                    var obj     = CloudCmd[moduleName],
                        func    = obj[funcName];

                    Util.exec(func, params);
                },

                function(callback) {
                    Util.exec(obj, callback);
                });
        };

        this.refresh                =  function(panelParam, options, callback) {
            var panel           = panelParam || Info.panel,
                NEEDREFRESH     = true,
                path            = DOM.getCurrentDirPath(panel),
                notSlashlLink   = CloudFunc.rmLastSlash(path);

            if (!callback) {
                callback    = options;
                options     = {};
            }

            if (typeof callback !== 'function')
                throw(Error('callback should be function!'));

            CloudCmd.loadDir({
                path        : notSlashlLink,
                isRefresh   : NEEDREFRESH,
                history     : false,
                panel       : panel,
                noCurrent   : options.noCurrent
            }, callback);
        };

        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param options
         * { refresh, history } - необходимость обновить данные о каталоге
         * @param panel
         * @param callback
         *
         */
        function ajaxLoad(path, options, panel, callback) {
            var create = function(error, json) {
                var RESTful     = DOM.RESTful,
                    name        = Info.name,
                    obj         = Util.json.parse(json),
                    isRefresh   = options.refresh,
                    noCurrent   = options.noCurrent,
                    history     = options.history;

                if (!isRefresh && json)
                    createFileTable(obj, panel, history, callback);
                else
                    RESTful.read(path, 'json', function(obj) {
                        createFileTable(obj, panel, history, function() {
                            var current;

                            if (isRefresh && !noCurrent) {
                                current = DOM.getCurrentByName(name);
                                DOM.setCurrentFile(current);
                            }

                            Util.exec(callback);
                        });
                        Storage.set(path, obj);
                    });
            };

            if (!options)
                options    = {};

            CloudCmd.log('reading dir: "' + path + '";');

            Files.get('config', function(error, config) {
                var dirStorage;

                if (error)
                    alert(error);
                else
                    dirStorage = config.dirStorage;

                if (dirStorage)
                    Storage.get(path, create);
                else
                    create();
            });
        }

        /**
         * Функция строит файловую таблицу
         * @param json  - данные о файлах
         * @param panelParam
         * @param history
         * @param callback
         */
        function createFileTable(json, panelParam, history, callback) {
            var names           = ['file', 'path', 'link', 'pathLink'];

            Files.get(names, function(error, templFile, templPath, templLink, templPathLink) {
                var Dialog  = DOM.Dialog,
                    current,
                    panel   = panelParam || Info.panel,
                    dir     = Info.dir,
                    name    = Info.name,
                    i       = panel.childNodes.length;

                if (error) {
                    Dialog.alert(error.responseText);
                } else {
                    while (i--)
                        panel.removeChild(panel.lastChild);

                    panel.innerHTML = CloudFunc.buildFromJSON({
                        data        : json,
                        id          : panel.id,
                        template    : {
                            file        : templFile,
                            path        : templPath,
                            pathLink    : templPathLink,
                            link        : templLink
                        }
                    });

                    Listeners.setOnPanel(panel.id);

                    if (name === '..' && dir !== '/')
                        current = DOM.getCurrentByName(dir);

                    if (!current)
                        current = DOM.getFiles(panel)[0];

                    DOM.setCurrentFile(current, {
                        history: history
                    });

                    Util.exec(callback);
                }
            });
        }

        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var current, name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files,

                fileTable   = {
                    path    : path,
                    files   : []
                },

                files       = fileTable.files,

                i, n        = infoFiles.length;

            for (i = 0; i < n; i++) {
                current     = infoFiles[i];
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);

                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            }

            ret = Util.json.stringify(fileTable);

            return ret;
        }

        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;

            if (path !== parentPath) {
                path    = parentPath;
                path    = CloudFunc.rmLastSlash(path);

                CloudCmd.loadDir({
                    path: path
                });
            }
        };

        function getStrBigFirst(str) {
            var first;

            if (!str)
                throw Error('str could not be empty!');

            first   = str[0].toUpperCase();
            str     = first + str.slice(1);

            return str;
        }

    }
})(this, Util, DOM, CloudFunc, join);
<commit_msg>feature(client) rm throw when no callback<commit_after>var Util, DOM, CloudFunc, join;

(function(scope, Util, DOM, CloudFunc, join) {
    'use strict';

    scope.CloudCmd = new CloudCmdProto(Util, DOM, CloudFunc);

    function CloudCmdProto(Util, DOM, CloudFunc) {
        var Key,
            Debug,
            log                     = function(str) {
                if (Debug)
                    console.log(str);
            },

            Listeners,
            Files                   = DOM.Files,
            Images                  = DOM.Images,
            Info                    = DOM.CurrentInfo,
            CloudCmd                = this,
            Storage                 = DOM.Storage,
            type                    = Util.type;

        this.log                    = log,
        this.PREFIX                 = '',
        this.MousePosition          = {};
        this.LIBDIR                 = '/lib/';
        this.LIBDIRCLIENT           = '/lib/client/';
        this.MIN_ONE_PANEL_WIDTH    = 1155;
        this.HOST                   = location.origin ||
                                      location.protocol + '//' + location.host;

        log.enable     = function() {
            Debug = true;
        };

        log.disable    = function() {
            Debug = false;
        };

        /**
         * Функция привязываеться ко всем ссылкам и
         *  загружает содержимое каталогов
         *
         * @param params - {
         *          paramLink - ссылка
         *          needRefresh - необходимость обязательной загрузки данных с сервера
         *          panel
         *      }
         * @param callback
         */
        this.loadDir                = function(params, callback) {
            var link, imgPosition, panelChanged, pathParams,
                isRefresh, panel, history,
                p               = params;

            if (params) {
                pathParams      = p.path;
                isRefresh       = p.isRefresh;
                panel           = p.panel;
                history         = p.history;
            }

            if (pathParams)
                link = pathParams;

            if (!params.noCurrent)
                if (panel && panel !== Info.panel) {
                    DOM.changePanel();
                    panelChanged = true;
                }

            if (panelChanged || isRefresh)
                imgPosition = 'top';

            Images.show.load(imgPosition);

            /* загружаем содержимое каталога */
            ajaxLoad(link, {
                refresh     : isRefresh,
                history     : history,
                noCurrent   : params.noCurrent
            }, panel, callback);
        };

        /**
         * function load modules
         * @pParams = {name, path, func, dobefore, arg}
         */
        function loadModule(params) {
            var name, path, func, doBefore,
                funcName, isContain;

            if (params) {
                name        = params.name,
                path        = params.path,
                func        = params.func,
                funcName    = params.funcName,
                doBefore    = params.dobefore;

                if (path && !name) {
                    name = getStrBigFirst(path);
                    name = name.replace(/.js$/, '');
                }

                isContain   = /\.js/.test(path);

                if (!isContain)
                    path += '.js';

                if (!CloudCmd[name]) {
                    CloudCmd[name] = function() {
                        var pathFull    = CloudCmd.LIBDIRCLIENT + path,
                            args        = arguments;

                        Util.exec(doBefore);

                        return DOM.load.js(pathFull, func ||
                            function(error) {
                                var Proto = CloudCmd[name];

                                if (!error && type.function(Proto))
                                    CloudCmd[name] = applyConstructor(Proto, args);
                            });
                    };

                    CloudCmd[name][funcName] = CloudCmd[name];
                }
            }
        }

        /*
         * apply arguemnts to constructor
         *
         * @param constructor
         * @param args
         */
        function applyConstructor(constructor, args) {
            var F = function () {
                return constructor.apply(this, args);
            };

            F.prototype = constructor.prototype;
            return new F();
        }

        /** Конструктор CloudClient, который
         * выполняет весь функционал по
         * инициализации
         */
        this.init                    = function(prefix) {
            var func        = function() {
                    Util.exec.series([
                        initModules,
                        baseInit,
                        function() {
                            CloudCmd.route(location.hash);
                        }
                    ]);
                },

                funcBefore  = function(callback) {
                    var src     = CloudCmd.LIBDIRCLIENT + 'polyfill.js';

                    DOM.loadJquery(function() {
                        DOM.load.js(src, callback);
                    });
                };

            CloudCmd.PREFIX = prefix;
            CloudCmd.LIBDIR         = prefix + '/lib/';
            CloudCmd.LIBDIRCLIENT   = prefix + '/lib/client/';

            Util.exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
        };

        this.join                   = function(urls) {
            var prefix  = CloudCmd.PREFIX;

            Util.check(arguments, ['urls']);

            urls = urls.map(function(url) {
                return url.replace(prefix, '');
            });

            return prefix + join(urls);
        };

        this.route                   = function(path) {
            var module, file, current, msg,
                query   = path.split('/');

            if (path) {
                module      = query[0];
                module      = module.slice(1).join('');
                module      = getStrBigFirst(module);

                file        = query[1];
                current     = DOM.getCurrentByName(file);

                if (file && !current) {
                    msg = CloudFunc.formatMsg('set current file', file, 'error');
                    CloudCmd.log(msg);
                } else {
                    DOM.setCurrentFile(current);
                    CloudCmd.execFromModule(module, 'show');
                }
            }
        };

        function initModules(callback) {
            Util.exec.if(CloudCmd.Key, function() {
                Key          = new CloudCmd.Key();
                CloudCmd.Key = Key;
                Key.bind();
            }, function(callback) {
                loadModule({
                    /* привязываем клавиши к функциям */
                    path    : 'key.js',
                    func    : callback
                });
            });

            Files.get('modules', function(error, modules) {
                var storageObj, mod, path,
                    STORAGE     = 'storage',
                    showLoad    = Images.show.load,

                    doBefore    = {
                        'edit'                  : showLoad,
                        'menu'                  : showLoad,
                        'storage/_filepicker'   : showLoad
                    },

                    load = function(name, path, func) {
                        var isTmpl = path === 'template';

                        loadModule({
                            name        : name,
                            funcName    : isTmpl ? 'get' : 'show',
                            path        : path,
                            dobefore    : func
                        });
                    };

                if (!modules)
                    modules = [];

                modules.forEach(function(module) {
                    var isStr = type.string(module);

                    if (isStr)
                        load(null, module, doBefore[module]);
                });

                storageObj = Util.findObjByNameInArr(modules, STORAGE),
                mod        = Util.getNamesFromObjArray(storageObj);

                mod.forEach(function(name) {
                    path = STORAGE + '/_' + name.toLowerCase();

                    load(name, path, doBefore[path]);
                });

                callback();
            });
        }

        function baseInit(callback) {
            var dirPath = '',
                files   = DOM.getFiles(),
                LEFT    = CloudFunc.PANEL_LEFT,
                RIGHT   = CloudFunc.PANEL_RIGHT;

            /* выделяем строку с первым файлом */
            if (files)
                DOM.setCurrentFile(files[0]);

            dirPath     = DOM.getCurrentDirPath(),
            Listeners   = CloudCmd.Listeners;
            Listeners.init();

            Listeners.setOnPanel(LEFT);
            Listeners.setOnPanel(RIGHT);

            Listeners.initKeysPanel();

            /* Устанавливаем кэш корневого каталога */
            dirPath     = CloudFunc.rmLastSlash(dirPath);

            Storage.get(dirPath, function(error, data) {
                if (!data) {
                    data    = getJSONfromFileTable();
                    Storage.set(dirPath, data);
                }
            });

            callback();
        }

        this.execFromModule         = function(moduleName, funcName, params) {
            var obj     = CloudCmd[moduleName],
                isObj   = Util.type.object(obj);

            Util.exec.if(isObj,
                function() {
                    var obj     = CloudCmd[moduleName],
                        func    = obj[funcName];

                    Util.exec(func, params);
                },

                function(callback) {
                    Util.exec(obj, callback);
                });
        };

        this.refresh                =  function(panelParam, options, callback) {
            var panel           = panelParam || Info.panel,
                NEEDREFRESH     = true,
                path            = DOM.getCurrentDirPath(panel),
                notSlashlLink   = CloudFunc.rmLastSlash(path);

            if (!callback) {
                callback    = options;
                options     = {};
            }

            CloudCmd.loadDir({
                path        : notSlashlLink,
                isRefresh   : NEEDREFRESH,
                history     : false,
                panel       : panel,
                noCurrent   : options.noCurrent
            }, callback);
        };

        /**
         * Функция загружает json-данные о Файловой Системе
         * через ajax-запрос.
         * @param path - каталог для чтения
         * @param options
         * { refresh, history } - необходимость обновить данные о каталоге
         * @param panel
         * @param callback
         *
         */
        function ajaxLoad(path, options, panel, callback) {
            var create = function(error, json) {
                var RESTful     = DOM.RESTful,
                    name        = Info.name,
                    obj         = Util.json.parse(json),
                    isRefresh   = options.refresh,
                    noCurrent   = options.noCurrent,
                    history     = options.history;

                if (!isRefresh && json)
                    createFileTable(obj, panel, history, callback);
                else
                    RESTful.read(path, 'json', function(obj) {
                        createFileTable(obj, panel, history, function() {
                            var current;

                            if (isRefresh && !noCurrent) {
                                current = DOM.getCurrentByName(name);
                                DOM.setCurrentFile(current);
                            }

                            Util.exec(callback);
                        });
                        Storage.set(path, obj);
                    });
            };

            if (!options)
                options    = {};

            CloudCmd.log('reading dir: "' + path + '";');

            Files.get('config', function(error, config) {
                var dirStorage;

                if (error)
                    alert(error);
                else
                    dirStorage = config.dirStorage;

                if (dirStorage)
                    Storage.get(path, create);
                else
                    create();
            });
        }

        /**
         * Функция строит файловую таблицу
         * @param json  - данные о файлах
         * @param panelParam
         * @param history
         * @param callback
         */
        function createFileTable(json, panelParam, history, callback) {
            var names           = ['file', 'path', 'link', 'pathLink'];

            Files.get(names, function(error, templFile, templPath, templLink, templPathLink) {
                var Dialog  = DOM.Dialog,
                    current,
                    panel   = panelParam || Info.panel,
                    dir     = Info.dir,
                    name    = Info.name,
                    i       = panel.childNodes.length;

                if (error) {
                    Dialog.alert(error.responseText);
                } else {
                    while (i--)
                        panel.removeChild(panel.lastChild);

                    panel.innerHTML = CloudFunc.buildFromJSON({
                        data        : json,
                        id          : panel.id,
                        template    : {
                            file        : templFile,
                            path        : templPath,
                            pathLink    : templPathLink,
                            link        : templLink
                        }
                    });

                    Listeners.setOnPanel(panel.id);

                    if (name === '..' && dir !== '/')
                        current = DOM.getCurrentByName(dir);

                    if (!current)
                        current = DOM.getFiles(panel)[0];

                    DOM.setCurrentFile(current, {
                        history: history
                    });

                    Util.exec(callback);
                }
            });
        }

        /**
         * Функция генерирует JSON из html-таблицы файлов и
         * используеться при первом заходе в корень
         */
        function getJSONfromFileTable() {
            var current, name, size, owner, mode, ret,
                path        = DOM.getCurrentDirPath(),
                infoFiles   = Info.files,

                fileTable   = {
                    path    : path,
                    files   : []
                },

                files       = fileTable.files,

                i, n        = infoFiles.length;

            for (i = 0; i < n; i++) {
                current     = infoFiles[i];
                name        = DOM.getCurrentName(current);
                size        = DOM.getCurrentSize(current);
                owner       = DOM.getCurrentOwner(current);
                mode        = DOM.getCurrentMode(current);

                if (name !== '..')
                    files.push({
                        name    : name,
                        size    : size,
                        mode    : mode,
                        owner   : owner
                    });
            }

            ret = Util.json.stringify(fileTable);

            return ret;
        }

        this.goToParentDir          = function() {
            var path        = Info.dirPath,
                parentPath  = Info.parentDirPath;

            if (path !== parentPath) {
                path    = parentPath;
                path    = CloudFunc.rmLastSlash(path);

                CloudCmd.loadDir({
                    path: path
                });
            }
        };

        function getStrBigFirst(str) {
            var first;

            if (!str)
                throw Error('str could not be empty!');

            first   = str[0].toUpperCase();
            str     = first + str.slice(1);

            return str;
        }

    }
})(this, Util, DOM, CloudFunc, join);
<|endoftext|>


<commit_before>var MapModule = require('ti.map');

function createMap() {
	var mapview = MapModule.createView({
		mapType: MapModule.NORMAL_TYPE,
		animate:true,
		regionFit:true,
		userLocation:true
	});

	return mapview;
}

function createAnnotation(params) {
	var pin = MapModule.createAnnotation({
		latitude: place.latitude,
		longitude: place.longitude,
		title: place.name,
		pincolor: MapModule.ANNOTATION_RED,
	});

	return pin;
}

exports.createAnnotation = createAnnotation;
exports.createMap = createMap;<commit_msg>Params wasn't even used<commit_after>var MapModule = require('ti.map');

function createMap() {
	var mapview = MapModule.createView({
		mapType: MapModule.NORMAL_TYPE,
		animate:true,
		regionFit:true,
		userLocation:true
	});

	return mapview;
}

function createAnnotation(params) {
	var pin = MapModule.createAnnotation({
		latitude: params.lat,
		longitude: params.lon,
		title: params.title,
		pincolor: MapModule.ANNOTATION_RED,
	});

	return pin;
}

exports.createAnnotation = createAnnotation;
exports.createMap = createMap;<|endoftext|>


<commit_before>/**
 * Configuration defaults
 */

class Config {
  constructor(options = {}) {
    this.blockLimit = 6718946;
    this.defaultGasPrice = 5;

    this.currency = options.currency || "eur";
    this.ethPrice = options.ethPrice || null;
    this.gasPrice = options.gasPrice || null;
    this.outputFile = options.outputFile || null;
    this.rst = options.rst || false;
    this.rstTitle = options.rstTitle || "";
    this.showTimeSpent = options.showTimeSpent || false;
    this.srcPath = options.src || "contracts";
    this.artifactType = options.artifactType || "truffle-v5";
    this.noColors = options.noColors;
    this.proxyResolver = options.proxyResolver || null;
    this.metadata = options.metadata || null;

    this.excludeContracts = Array.isArray(options.excludeContracts)
      ? options.excludeContracts
      : [];

    this.onlyCalledMethods = options.onlyCalledMethods === false ? false : true;

    this.url = options.url
      ? this._normalizeUrl(options.url)
      : this.resolveClientUrl();
  }

  /**
   * Tries to obtain the client url reporter's sync-requests will target.
   * @return {String}         url e.g http://localhost:8545
   */
  resolveClientUrl() {
    // Case: web3 globally available in mocha test context
    try {
      if (web3 && web3.currentProvider) {
        const cp = web3.currentProvider;

        // Truffle/Web3 http
        if (cp.host) return cp.host;

        // Truffle/Web3 websockets
        if (cp.connection) return this._normalizeUrl(cp.connection.url);
      }
    } catch (err) {
      // Web3 undefined
    }

    // Case: Failure
    const msg =
      `ERROR: eth-gas-reporter was unable to resolve a client url ` +
      `from the provider available in your test context. Try setting the ` +
      `url as a mocha reporter option (ex: url='http://localhost:8545')`;

    console.log(message);
    process.exit(1);
  }

  /**
   * Forces websockets to http
   * @param  {String} url e.g web3.provider.connection.url
   * @return {String}     http:// prefixed url
   */
  _normalizeUrl(url) {
    return url.replace("ws://", "http://");
  }
}

module.exports = Config;
<commit_msg>Fix variable name mismatch<commit_after>/**
 * Configuration defaults
 */

class Config {
  constructor(options = {}) {
    this.blockLimit = 6718946;
    this.defaultGasPrice = 5;

    this.currency = options.currency || "eur";
    this.ethPrice = options.ethPrice || null;
    this.gasPrice = options.gasPrice || null;
    this.outputFile = options.outputFile || null;
    this.rst = options.rst || false;
    this.rstTitle = options.rstTitle || "";
    this.showTimeSpent = options.showTimeSpent || false;
    this.srcPath = options.src || "contracts";
    this.artifactType = options.artifactType || "truffle-v5";
    this.noColors = options.noColors;
    this.proxyResolver = options.proxyResolver || null;
    this.metadata = options.metadata || null;

    this.excludeContracts = Array.isArray(options.excludeContracts)
      ? options.excludeContracts
      : [];

    this.onlyCalledMethods = options.onlyCalledMethods === false ? false : true;

    this.url = options.url
      ? this._normalizeUrl(options.url)
      : this.resolveClientUrl();
  }

  /**
   * Tries to obtain the client url reporter's sync-requests will target.
   * @return {String}         url e.g http://localhost:8545
   */
  resolveClientUrl() {
    // Case: web3 globally available in mocha test context
    try {
      if (web3 && web3.currentProvider) {
        const cp = web3.currentProvider;

        // Truffle/Web3 http
        if (cp.host) return cp.host;

        // Truffle/Web3 websockets
        if (cp.connection) return this._normalizeUrl(cp.connection.url);
      }
    } catch (err) {
      // Web3 undefined
    }

    // Case: Failure
    const message =
      `ERROR: eth-gas-reporter was unable to resolve a client url ` +
      `from the provider available in your test context. Try setting the ` +
      `url as a mocha reporter option (ex: url='http://localhost:8545')`;

    console.log(message);
    process.exit(1);
  }

  /**
   * Forces websockets to http
   * @param  {String} url e.g web3.provider.connection.url
   * @return {String}     http:// prefixed url
   */
  _normalizeUrl(url) {
    return url.replace("ws://", "http://");
  }
}

module.exports = Config;
<|endoftext|>


<commit_before>const merge = require('deepmerge')
const path = require('path')

const DEFAULT_CONFIG = {
  source: process.cwd(),
  destination: path.join(process.cwd(), '_site'),
  exclude: [
    'package.json',
    'node_modules/',
    'bower_components/',
    'coverage/'
  ]
}

function generateConfig (conf = {}) {
  const result = merge(DEFAULT_CONFIG, conf)
  if (conf.destination) {
    result.exclude.push(path.relative(process.cwd(), conf.destination))
  }
  return result
}

function mergeConfigs (conf1, conf2) {
  return merge(conf1, conf2)
}

module.exports = {
  DEFAULT_CONFIG,
  generateConfig,
  mergeConfigs
}
<commit_msg>exclude _site by default<commit_after>const merge = require('deepmerge')
const path = require('path')

const DEFAULT_CONFIG = {
  source: process.cwd(),
  destination: path.join(process.cwd(), '_site'),
  exclude: [
    'package.json',
    'node_modules/',
    'bower_components/',
    'coverage/',
    '_site'
  ]
}

function generateConfig (conf = {}) {
  const result = merge(DEFAULT_CONFIG, conf)
  if (conf.destination) {
    result.exclude.push(path.relative(process.cwd(), conf.destination))
  }
  return result
}

function mergeConfigs (conf1, conf2) {
  return merge(conf1, conf2)
}

module.exports = {
  DEFAULT_CONFIG,
  generateConfig,
  mergeConfigs
}
<|endoftext|>


<commit_before>var util = require('util');
var events = require('events');
var async = require('async');
var broadway = require('broadway');
var App = require('./app.js');
var _ = require('lodash');
var pluginUtil = require('./pluginutil.js');
var	mixdownLogger = require('./logger');

/**
 *
 *	Config - loads and manages configuration for environment and sites.
 * 	@param config - Object containing the server configuration.
**/
var Config = function(config) {
	this.config = config;
	this._externalConfig = null;
	this.services = null;

	if (!this.config.app) {
		throw new Error('There is no default app to initialize.');
	}

	this.config.app.id = 'default';
};

util.inherits(Config, events.EventEmitter);

Config.prototype.init = function(callback) {
	this.initLogger();

	// initialize services (sites or cli apps)
	var that = this;
	this.getExternalConfig(function(err, extConfig) {

		if (!extConfig && !err) {
			err = new Error('External config was not loaded.  This app likely won\'t function');
		}

		if (err) {
			_.isFunction(callback) ? callback(err) : null;
			return;
		}

		extConfig.getServices(function(err, services) {

			// Do not bubble exception on error here.  Just log the error and continue.
			if (err && logger) {
				logger.error(err);
			}

			that.services = services && services.length ? services : [that.config.app];
			that.initServices(callback);
		});

	});
};

Config.prototype.initLogger = function() {

	if (global.logger) {
		global.logger.clear();
	}

	var loggerConfig = this.config.logger || {
    "defaults": {
      "handleExceptions": true,
      "json": false,
      "timestamp": true,
      "colorize": true,
      "prettyPrint": true
    },
    "transports": [{ "transport": "Console" }]
  };

	// Init logger: Need to move this to external place where is can be injected with
	// alpha, beta, and prod settings
	global.logger = mixdownLogger.create(this.config.logger);

	if (!logger) {
		console.error('There is no logger declared.');
	}

};

Config.prototype.getExternalConfig = function(callback) {

	if (this.services && this._externalConfig) {
		_.isFunction(callback) ? callback(null, this._externalConfig) : null;
		return;
	}

	// new bw to attach dist config module.
	var app = new broadway.App();
	var that = this;

	// resolve and attach dist config module.
	pluginUtil.use({
		plugin: _.defaults(this.config.services || {}, { module: "mixdown-config-filesystem" }),
		app: { plugins: app } // YUCK:  this is opaque
	});

	// initialize dist config module
	app.init(function(err) {

		if (!err) {
			that._externalConfig = app.externalConfig;
		}
		_.isFunction(callback) ? callback(err, that._externalConfig) : null;

	});
};

Config.prototype.initServices = function(callback) {
	var apps = {};
	var inits = [];
	var defaultApp = this.config.app;
	var main = this.config.main;
	var that = this;

	// enumerate and create the apps
	_.each(this.services, function(service) {

		_.bind(mergeSection, service, defaultApp, 'plugins')();
		var app = new App(service);

		app.main = main;
		app.vhosts = service.vhosts;
		app.on('error', function(err) {
			that.emit('error', err);
		});

		apps[service.id] = app;

		// enqueue the app init so we can attach a cb to the entire thing.
		inits.push(_.bind(app.init, app));
	});

	// async the inits and notify when they are done.
	// TODO: see if a single app failure could cause all apps to fail.
	async.parallel(inits, function(err) {
		if (!err) {
			that.apps = apps;
		}
		_.isFunction(callback) ? callback(err, that) : null;
	});

};

/**
* Applies the overrides from the env config.  Useful for setting a base config, then applying configuration overrides.
* @param env - String representing the env overrides for this config.  This will load the config
**/
Config.prototype.env = function(env) {

	// apply merge of plugins at app level
	if (env && env.app) {

		// merge the core app
		_.bind(mergeSection, this.config.app, env.app, 'plugins', true)();

		// override the vhosts with the env specific hosts.
		if (env.app.vhosts) {
			this.config.app.vhosts = env.app.vhosts;
		}

		// logger does not get merged.
		if (env.logger) {
			this.config.logger = env.logger;
		}

		// main does not get merged.
		if (env.main) {
			this.config.main = env.main;
		}

		// services plugin config does not get merged.
		if (env.services) {
			this.config.services = env.services;
		}
	}

};

Config.prototype.stop = function(callback) {

	if (this.main) {
		this.main.stop(callback);
	}

	return this;
};

Config.prototype.start = function(callback) {
	var mainConfig = this.config.main;

	// resolve main module.
	var mainFactory = pluginUtil.require({ plugin: mainConfig });

	// Main modules are factories so call create()
	var main = this.main = mainFactory.create(this, mainConfig.options);

	this.init(function(errInit) {
		if (errInit) {
			_.isFunction(callback) ? callback(errInit) : null;
			return;
		}

		// app  is ready and initialized, call start
		main.start(callback);

	});

	return this;
};

// This is a custom merge which causes arrays in config to be replaced instead of merged.
// http://lodash.com/docs#merge
var mixdownPluginOptionsMerge = function(a, b) {
  return _.isArray(a) ? b : undefined;
};

// create local utility function to apply the merge.
var mergeSection = function(parent, section, reverse) {
	var that = this,
		parentSection = parent[section] || {},
		thisSection = this[section] || {},
		keys = _.keys(parentSection).concat(_.keys(thisSection)),
		newSection = {};

	_.each(keys, function(key) {
		var thisThing = (thisSection[key] || {}),
			parentThing = (parentSection[key] || {});

		// if the property explicitly exists, but is null then do not pull the parent version and do not add to object
		if (thisSection.hasOwnProperty(key) && thisSection[key] === null) {
			// do nothing.
		}
		// when the section/key prop exists, but is not explicitly null then we want to merge the props.
		else if (reverse) {
			newSection[key] = _.clone(parentThing);
			newSection[key].module = newSection[key].module || thisThing.module;
			newSection[key].options = _.merge( _.cloneDeep(thisThing.options) || {}, newSection[key].options, mixdownPluginOptionsMerge);
		}
		else {
			newSection[key] = _.clone(thisThing);
			newSection[key].module = newSection[key].module || parentThing.module;
			newSection[key].options = _.merge( _.cloneDeep(parentThing.options) || {}, newSection[key].options, mixdownPluginOptionsMerge);
		}

	});

	this[section] = newSection;

};

module.exports = Config;

<commit_msg>added overlays for app configurations to allow common tasks to be remixed<commit_after>var util = require('util');
var events = require('events');
var async = require('async');
var broadway = require('broadway');
var App = require('./app.js');
var _ = require('lodash');
var pluginUtil = require('./pluginutil.js');
var	mixdownLogger = require('./logger');

/**
 *
 *	Config - loads and manages configuration for environment and sites.
 * 	@param config - Object containing the server configuration.
**/
var Config = function(config) {
	this.config = config;
	this._externalConfig = null;
  this.services = null;
  this.overlays = null;

	if (!this.config.app) {
		throw new Error('There is no default app to initialize.');
	}

	this.config.app.id = 'default';
};

util.inherits(Config, events.EventEmitter);

Config.prototype.init = function(callback) {
	this.initLogger();

	// initialize services (sites or cli apps)
	var that = this;
	this.getExternalConfig(function(err, extConfig) {

		if (!extConfig && !err) {
			err = new Error('External config was not loaded.  This app likely won\'t function');
		}

		if (err) {
			_.isFunction(callback) ? callback(err) : null;
			return;
		}

		extConfig.getServices(function(err, services) {

			// Do not bubble exception on error here.  Just log the error and continue.
			if (err && logger) {
				logger.error(err);
			}

      if (services && services.length) {
        that.overlays = _.filter(services, function(s) { return s.overlay === true; });
        that.services = _.reject(services, function(s) { return s.overlay === true; });
      }
      else {
        that.services = [that.config.app];
      }

			that.initServices(callback);
		});

	});
};

Config.prototype.initLogger = function() {

	if (global.logger) {
		global.logger.clear();
	}

	var loggerConfig = this.config.logger || {
    "defaults": {
      "handleExceptions": true,
      "json": false,
      "timestamp": true,
      "colorize": true,
      "prettyPrint": true
    },
    "transports": [{ "transport": "Console" }]
  };

	// Init logger: Need to move this to external place where is can be injected with
	// alpha, beta, and prod settings
	global.logger = mixdownLogger.create(this.config.logger);

	if (!logger) {
		console.error('There is no logger declared.');
	}

};

Config.prototype.getExternalConfig = function(callback) {

	if (this.services && this._externalConfig) {
		_.isFunction(callback) ? callback(null, this._externalConfig) : null;
		return;
	}

	// new bw to attach dist config module.
	var app = new broadway.App();
	var that = this;

	// resolve and attach dist config module.
	pluginUtil.use({
		plugin: _.defaults(this.config.services || {}, { module: "mixdown-config-filesystem" }),
		app: { plugins: app } // YUCK:  this is opaque
	});

	// initialize dist config module
	app.init(function(err) {

		if (!err) {
			that._externalConfig = app.externalConfig;
		}
		_.isFunction(callback) ? callback(err, that._externalConfig) : null;

	});
};

Config.prototype.initServices = function(callback) {
	var apps = {};
	var inits = [];
	var defaultApp = this.config.app;
	var main = this.config.main;
	var that = this;
  var appConfig = _.cloneDeep(defaultApp);

	// enumerate and create the apps
	_.each(this.overlays, function(overlay) {
    _.bind(mergeSection, appConfig, overlay, 'plugins')();
	});

  // enumerate and create the apps
  _.each(this.services, function(service) {

    _.bind(mergeSection, service, appConfig, 'plugins')();
    var app = new App(service);

    app.main = main;
    app.vhosts = service.vhosts;
    app.on('error', function(err) {
      that.emit('error', err);
    });

    apps[service.id] = app;

    // enqueue the app init so we can attach a cb to the entire thing.
    inits.push(_.bind(app.init, app));
  });

	// async the inits and notify when they are done.
	// TODO: see if a single app failure could cause all apps to fail.
	async.parallel(inits, function(err) {
		if (!err) {
			that.apps = apps;
		}
		_.isFunction(callback) ? callback(err, that) : null;
	});

};

/**
* Applies the overrides from the env config.  Useful for setting a base config, then applying configuration overrides.
* @param env - String representing the env overrides for this config.  This will load the config
**/
Config.prototype.env = function(env) {

	// apply merge of plugins at app level
	if (env && env.app) {

		// merge the core app
		_.bind(mergeSection, this.config.app, env.app, 'plugins', true)();

		// override the vhosts with the env specific hosts.
		if (env.app.vhosts) {
			this.config.app.vhosts = env.app.vhosts;
		}

		// logger does not get merged.
		if (env.logger) {
			this.config.logger = env.logger;
		}

		// main does not get merged.
		if (env.main) {
			this.config.main = env.main;
		}

		// services plugin config does not get merged.
		if (env.services) {
			this.config.services = env.services;
		}
	}

};

Config.prototype.stop = function(callback) {

	if (this.main) {
		this.main.stop(callback);
	}

	return this;
};

Config.prototype.start = function(callback) {
	var mainConfig = this.config.main;

	// resolve main module.
	var mainFactory = pluginUtil.require({ plugin: mainConfig });

	// Main modules are factories so call create()
	var main = this.main = mainFactory.create(this, mainConfig.options);

	this.init(function(errInit) {
		if (errInit) {
			_.isFunction(callback) ? callback(errInit) : null;
			return;
		}

		// app  is ready and initialized, call start
		main.start(callback);

	});

	return this;
};

// This is a custom merge which causes arrays in config to be replaced instead of merged.
// http://lodash.com/docs#merge
var mixdownPluginOptionsMerge = function(a, b) {
  return _.isArray(a) ? b : undefined;
};

// create local utility function to apply the merge.
var mergeSection = function(parent, section, reverse) {
	var that = this,
		parentSection = parent[section] || {},
		thisSection = this[section] || {},
		keys = _.keys(parentSection).concat(_.keys(thisSection)),
		newSection = {};

	_.each(keys, function(key) {
		var thisThing = (thisSection[key] || {}),
			parentThing = (parentSection[key] || {});

		// if the property explicitly exists, but is null then do not pull the parent version and do not add to object
		if (thisSection.hasOwnProperty(key) && thisSection[key] === null) {
			// do nothing.
		}
		// when the section/key prop exists, but is not explicitly null then we want to merge the props.
		else if (reverse) {
			newSection[key] = _.clone(parentThing);
			newSection[key].module = newSection[key].module || thisThing.module;
			newSection[key].options = _.merge( _.cloneDeep(thisThing.options) || {}, newSection[key].options, mixdownPluginOptionsMerge);
		}
		else {
			newSection[key] = _.clone(thisThing);
			newSection[key].module = newSection[key].module || parentThing.module;
			newSection[key].options = _.merge( _.cloneDeep(parentThing.options) || {}, newSection[key].options, mixdownPluginOptionsMerge);
		}

	});

	this[section] = newSection;

};

module.exports = Config;

<|endoftext|>