<commit_before>//
// Module dependencies
//
var fs = require('fs'),
    path = require('path'),
    connect = require('connect'),
    express = require('express'),
    engines = require('consolidate'),
    i18n = require('i18next'),
    urljoin = require('./lib/urljoin'),
    _ = require('lodash'),
    logger = require('./lib/logger'),
    settings = require('./config/settings');

// Auto-load bundled middleware
var middleware = {};

fs.readdirSync(__dirname + '/lib/middleware').forEach(function(filename) {
    if ( ! /\.js$/.test(filename)) {
        return;
    }
    var name = path.basename(filename, '.js');
    middleware[name] = require('./lib/middleware/' + name);
});

module.exports = function(options) {
    options = options || {};

    // Change route settings in multihost environment
    settings.route = options.route || settings.route;

    console.log(settings.route);

    // Main app
    var app = express();
    var errorhandler = require('errorhandler');
    var favicon = require('serve-favicon');
    var cookieParser = require('cookie-parser');
    var bodyParser = require('body-parser');
    //var busboy = require('connect-busboy');
    var multiparty = require('connect-multiparty');
    var methodOverride = require('method-override');
    var morgan = require('morgan');
    var compress = require('compression');
    var serveStatic = require('serve-static');

    // Setup logger (winston)
    logger.init(settings.winston);
    logger.registerAppHelper(app); // Register app.locals (app.helper)

    var log = logger();

    // Setup i18n (i18next)
    i18n.init(settings.i18next);
    i18n.registerAppHelper(app); // Register app.locals (app.helper)

    // Settings
    (function(app) {
        var env = process.env.NODE_ENV || 'development';
        if ('development' === env) {
            // Error handler - https://github.com/expressjs/errorhandler
            // Development error handler, providing stack traces and error message responses
            // for requests accepting text, html, or json.
            app.use(errorhandler());

            // a custom "verbose errors" setting which can be used in the templates via settings['verbose errors']
            app.enable('verbose errors'); // Enables verbose errors in development
            app.disable('view cache'); // Disables view template compilation caching in development
        } else {
            // a custom "verbose errors" setting which can be used in the templates via settings['verbose errors']
            app.disable('verbose errors'); // Disables verbose errors in production
            app.enable('view cache'); // Enables view template compilation caching in production
        }

        app.enable('trust proxy'); // Enables reverse proxy support, disabled by default
        app.enable('case sensitive routing'); // Enable case sensitivity, disabled by default, treating "/Foo" and "/foo" as the same
        app.disable('strict routing'); // Enable strict routing, by default "/foo" and "/foo/" are treated the same by the router
        app.disable('x-powered-by'); // Enables the X-Powered-By: Express HTTP header, enabled by default

        for (var i = 0; i < settings.view.engines.length; ++i) {
            var extension = settings.view.engines[i].extension;
            var template = settings.view.engines[i].template;
            app.engine(extension, engines[template]);
        }
        app.set('view engine', settings.view.defaultExtension); // The default engine extension to use when omitted
        app.set('views', path.join(__dirname, 'views')); // The view directory path

        log.info('app.settings: %j', app.settings);
    }(app));

    // Removes the 'X-Powered-By' header in earlier versions of Express
    app.use(function (req, res, next) {
        res.removeHeader('X-Powered-By');
        next();
    });

    // Cross-origin resource sharing
    //app.use(middleware.cors());

    // Middleware
    // https://github.com/senchalabs/connect
    app.use(favicon(path.join(__dirname, '../web/favicon.ico')));
    app.use(cookieParser());

    // Connect's body parsing middleware. This only handles urlencoded and json bodies.
    // https://github.com/expressjs/body-parser
    app.use(bodyParser.json(settings.middleware['body-parser']['json']));
    app.use(bodyParser.urlencoded(settings.middleware['body-parser']['urlencoded']));

    // For multipart bodies, please use the following modules:
    // - [busboy](https://github.com/mscdex/busboy) and [connect-busboy](https://github.com/mscdex/connect-busboy)
    // - [multiparty](https://github.com/andrewrk/node-multiparty) and [connect-multiparty](https://github.com/andrewrk/connect-multiparty)
    app.use(multiparty(settings.middleware['multiparty']));

    // https://github.com/dominictarr/connect-restreamer
    // connect's bodyParser has a problem when using it with a proxy.
    // It gobbles up all the body events, so that the proxy doesn't see anything!
    app.use(require('connect-restreamer')());

    app.use(methodOverride());
    app.use(morgan(settings.middleware['morgan']));
    app.use(compress(settings.middleware['compression']));

    _.each(settings.assets, function(asset, name) {
        log.info('assets: name=%s, asset=%s', name, JSON.stringify(asset));

        if ( ! asset.path) {
            log.error('asset path is not defined');
            return;
        }

        _.each(asset.routes, function(asset_route) {
            var route = urljoin(settings.route || '/', asset_route || '');
            log.info('> route=%s', name, route);
            app.use(route, serveStatic(asset.path, {
                maxAge: asset.maxAge
            }));
        });
    });

    // i18n routing
    app.use(i18n.handle);

    // https://github.com/visionmedia/express/wiki/New-features-in-4.x
    // No more app.use(app.router)
    // All routing methods will be added in the order in which they appear. You should not do app.use(app.router). This eliminates the most common issue with Express.
    var routes = require('./routes/main');
    routes.init(app);

    // Error handling
    console.assert( ! _.isUndefined(middleware.err_log), 'lib/middleware/err_log not found');
    console.assert( ! _.isUndefined(middleware.err_client), 'lib/middleware/err_client not found');
    console.assert( ! _.isUndefined(middleware.err_notfound), 'lib/middleware/err_notfound not found');
    console.assert( ! _.isUndefined(middleware.err_server), 'lib/middleware/err_server not found');

    app.use(middleware.err_log());
    app.use(middleware.err_client({
        error: 'XHR error'
    }));
    app.use(middleware.err_notfound({
        view: path.join('common', '404.hogan'),
        error: 'Not found'
    }));
    app.use(middleware.err_server({
        view: path.join('common', '500.jade'),
        error: 'Internal server error'
    }));

    return app;
};
<commit_msg>Remove unused code snippet<commit_after>//
// Module dependencies
//
var fs = require('fs'),
    path = require('path'),
    connect = require('connect'),
    express = require('express'),
    engines = require('consolidate'),
    i18n = require('i18next'),
    urljoin = require('./lib/urljoin'),
    _ = require('lodash'),
    logger = require('./lib/logger'),
    settings = require('./config/settings');

// Auto-load bundled middleware
var middleware = {};

fs.readdirSync(__dirname + '/lib/middleware').forEach(function(filename) {
    if ( ! /\.js$/.test(filename)) {
        return;
    }
    var name = path.basename(filename, '.js');
    middleware[name] = require('./lib/middleware/' + name);
});

module.exports = function(options) {
    options = options || {};

    // Change route settings in multihost environment
    settings.route = options.route || settings.route;

    // Main app
    var app = express();
    var errorhandler = require('errorhandler');
    var favicon = require('serve-favicon');
    var cookieParser = require('cookie-parser');
    var bodyParser = require('body-parser');
    //var busboy = require('connect-busboy');
    var multiparty = require('connect-multiparty');
    var methodOverride = require('method-override');
    var morgan = require('morgan');
    var compress = require('compression');
    var serveStatic = require('serve-static');

    // Setup logger (winston)
    logger.init(settings.winston);
    logger.registerAppHelper(app); // Register app.locals (app.helper)

    var log = logger();

    // Setup i18n (i18next)
    i18n.init(settings.i18next);
    i18n.registerAppHelper(app); // Register app.locals (app.helper)

    // Settings
    (function(app) {
        var env = process.env.NODE_ENV || 'development';
        if ('development' === env) {
            // Error handler - https://github.com/expressjs/errorhandler
            // Development error handler, providing stack traces and error message responses
            // for requests accepting text, html, or json.
            app.use(errorhandler());

            // a custom "verbose errors" setting which can be used in the templates via settings['verbose errors']
            app.enable('verbose errors'); // Enables verbose errors in development
            app.disable('view cache'); // Disables view template compilation caching in development
        } else {
            // a custom "verbose errors" setting which can be used in the templates via settings['verbose errors']
            app.disable('verbose errors'); // Disables verbose errors in production
            app.enable('view cache'); // Enables view template compilation caching in production
        }

        app.enable('trust proxy'); // Enables reverse proxy support, disabled by default
        app.enable('case sensitive routing'); // Enable case sensitivity, disabled by default, treating "/Foo" and "/foo" as the same
        app.disable('strict routing'); // Enable strict routing, by default "/foo" and "/foo/" are treated the same by the router
        app.disable('x-powered-by'); // Enables the X-Powered-By: Express HTTP header, enabled by default

        for (var i = 0; i < settings.view.engines.length; ++i) {
            var extension = settings.view.engines[i].extension;
            var template = settings.view.engines[i].template;
            app.engine(extension, engines[template]);
        }
        app.set('view engine', settings.view.defaultExtension); // The default engine extension to use when omitted
        app.set('views', path.join(__dirname, 'views')); // The view directory path

        log.info('app.settings: %j', app.settings);
    }(app));

    // Removes the 'X-Powered-By' header in earlier versions of Express
    app.use(function (req, res, next) {
        res.removeHeader('X-Powered-By');
        next();
    });

    // Cross-origin resource sharing
    //app.use(middleware.cors());

    // Middleware
    // https://github.com/senchalabs/connect
    app.use(favicon(path.join(__dirname, '../web/favicon.ico')));
    app.use(cookieParser());

    // Connect's body parsing middleware. This only handles urlencoded and json bodies.
    // https://github.com/expressjs/body-parser
    app.use(bodyParser.json(settings.middleware['body-parser']['json']));
    app.use(bodyParser.urlencoded(settings.middleware['body-parser']['urlencoded']));

    // For multipart bodies, please use the following modules:
    // - [busboy](https://github.com/mscdex/busboy) and [connect-busboy](https://github.com/mscdex/connect-busboy)
    // - [multiparty](https://github.com/andrewrk/node-multiparty) and [connect-multiparty](https://github.com/andrewrk/connect-multiparty)
    app.use(multiparty(settings.middleware['multiparty']));

    // https://github.com/dominictarr/connect-restreamer
    // connect's bodyParser has a problem when using it with a proxy.
    // It gobbles up all the body events, so that the proxy doesn't see anything!
    app.use(require('connect-restreamer')());

    app.use(methodOverride());
    app.use(morgan(settings.middleware['morgan']));
    app.use(compress(settings.middleware['compression']));

    _.each(settings.assets, function(asset, name) {
        log.info('assets: name=%s, asset=%s', name, JSON.stringify(asset));

        if ( ! asset.path) {
            log.error('asset path is not defined');
            return;
        }

        _.each(asset.routes, function(asset_route) {
            var route = urljoin(settings.route || '/', asset_route || '');
            log.info('> route=%s', name, route);
            app.use(route, serveStatic(asset.path, {
                maxAge: asset.maxAge
            }));
        });
    });

    // i18n routing
    app.use(i18n.handle);

    // https://github.com/visionmedia/express/wiki/New-features-in-4.x
    // No more app.use(app.router)
    // All routing methods will be added in the order in which they appear. You should not do app.use(app.router). This eliminates the most common issue with Express.
    var routes = require('./routes/main');
    routes.init(app);

    // Error handling
    console.assert( ! _.isUndefined(middleware.err_log), 'lib/middleware/err_log not found');
    console.assert( ! _.isUndefined(middleware.err_client), 'lib/middleware/err_client not found');
    console.assert( ! _.isUndefined(middleware.err_notfound), 'lib/middleware/err_notfound not found');
    console.assert( ! _.isUndefined(middleware.err_server), 'lib/middleware/err_server not found');

    app.use(middleware.err_log());
    app.use(middleware.err_client({
        error: 'XHR error'
    }));
    app.use(middleware.err_notfound({
        view: path.join('common', '404.hogan'),
        error: 'Not found'
    }));
    app.use(middleware.err_server({
        view: path.join('common', '500.jade'),
        error: 'Internal server error'
    }));

    return app;
};
<|endoftext|>


<commit_before>'use strict';

// resolve relative to build/config
var path = require('path').resolve.bind(null, __dirname);

module.exports = {
  web: {
    context: path('../../src'),
    entry: {
      web: './runtime/web/index.js',
    },
    output: {
      path: path('../dist'),
      filename: '[name]/l20n.js',
      libraryTarget: 'this',

    },
    module: {
      loaders: [{
        test: /\.js$/,
        include: [
          path('../../src')
        ],
        loader: 'babel',
      }]
    }
  },
  gaia: {
    context: path('../../src'),
    entry: {
      gaia: './runtime/web/index.js',
    },
    output: {
      path: path('../dist'),
      filename: '[name]/l20n.js',
      libraryTarget: 'this',

    },
    module: {
      loaders: [{
        test: /\.js$/,
        include: [
          path('../../src')
        ],
        loader: 'babel',
        query: {
          comments: false,
          whitelist: [
            'strict',
            'es6.modules',
            'es6.destructuring',
            'es6.arrowFunctions',
            'es6.properties.shorthand',
            'es6.forOf',
            'es6.spread',
            'es6.blockScoping'
          ],
        }
      }]
    }
  },
  node: {
    context: path('../../src'),
    entry: './bindings/node/index.js',
    output: {
      path: path('../dist'),
      filename: './node/l20n.js',
      libraryTarget: 'commonjs2',
    },
    externals: {
      'fs': true
    },
    module: {
      loaders: [{
        test: /\.js$/,
        include: [
          path('../../src')
        ],
        loader: 'babel'
      }]
    }
  },
};
<commit_msg>Add es6.parameters.rest<commit_after>'use strict';

// resolve relative to build/config
var path = require('path').resolve.bind(null, __dirname);

module.exports = {
  web: {
    context: path('../../src'),
    entry: {
      web: './runtime/web/index.js',
    },
    output: {
      path: path('../dist'),
      filename: '[name]/l20n.js',
      libraryTarget: 'this',

    },
    module: {
      loaders: [{
        test: /\.js$/,
        include: [
          path('../../src')
        ],
        loader: 'babel',
      }]
    }
  },
  gaia: {
    context: path('../../src'),
    entry: {
      gaia: './runtime/web/index.js',
    },
    output: {
      path: path('../dist'),
      filename: '[name]/l20n.js',
      libraryTarget: 'this',

    },
    module: {
      loaders: [{
        test: /\.js$/,
        include: [
          path('../../src')
        ],
        loader: 'babel',
        query: {
          comments: false,
          whitelist: [
            'strict',
            'es6.modules',
            'es6.destructuring',
            'es6.arrowFunctions',
            'es6.properties.shorthand',
            'es6.forOf',
            'es6.spread',
            'es6.parameters.rest',
            'es6.blockScoping'
          ],
        }
      }]
    }
  },
  node: {
    context: path('../../src'),
    entry: './bindings/node/index.js',
    output: {
      path: path('../dist'),
      filename: './node/l20n.js',
      libraryTarget: 'commonjs2',
    },
    externals: {
      'fs': true
    },
    module: {
      loaders: [{
        test: /\.js$/,
        include: [
          path('../../src')
        ],
        loader: 'babel'
      }]
    }
  },
};
<|endoftext|>


<commit_before>'use strict';
/*globals MozWebSocket */

/**
 * Minimum viable WebSocket client. This function is stringified and written in
 * to our client side library.
 *
 * @runat client
 * @api private
 */
module.exports = function client() {
  var primus = this
    , socket;

  //
  // Selects an available WebSocket constructor.
  //
  var Factory = (function factory() {
    if ('undefined' !== typeof WebSocket) return WebSocket;
    if ('undefined' !== typeof MozWebSocket) return MozWebSocket;

    try { return Primus.require('faye-websocket').Client; }
    catch (e) {}

    return undefined;
  })();

  if (!Factory) return primus.critical(new Error('Missing required `faye-websocket` module. Please run `npm install --save faye-websocket`'));

  //
  // Connect to the given URL.
  //
  primus.on('outgoing::open', function opening() {
    if (socket) socket.close();

    //
    // FireFox will throw an error when we try to establish a connection from
    // a secure page to an unsecured WebSocket connection. This is inconsistent
    // behaviour between different browsers. This should ideally be solved in
    // Primus when we connect.
    //
    try {
      //
      // Only allow primus.transport object in Node.js, it will throw in
      // browsers with a TypeError if we supply to much arguments.
      //
      var prot = primus.url.protocol === 'ws+unix:' ? 'ws+unix' : 'ws';
      if (Factory.length === 3) {
        primus.socket = socket = new Factory(
          primus.uri({ protocol: prot, query: true }),  // URL
          [],                                           // Sub protocols
          primus.transport                              // options.
        );
      } else {
        primus.socket = socket = new Factory(primus.uri({ protocol: prot, query: true }));
      }
    } catch (e) { return primus.emit('error', e); }

    //
    // Setup the Event handlers.
    //
    socket.binaryType = 'arraybuffer';
    socket.onopen = primus.emits('open');
    socket.onerror = primus.emits('error');
    socket.onclose = primus.emits('end');
    socket.onmessage = primus.emits('data', function parse(evt) {
      return evt.data;
    });
  });

  //
  // We need to write a new message to the socket.
  //
  primus.on('outgoing::data', function write(message) {
    if (!socket || socket.readyState !== 1) return;

    try { socket.send(message); }
    catch (e) { primus.emit('incoming::error', e); }
  });

  //
  // Attempt to reconnect the socket. It assumes that the `outgoing::end` event is
  // called if it failed to disconnect.
  //
  primus.on('outgoing::reconnect', function reconnect() {
    if (socket) primus.emit('outgoing::end');
    primus.emit('outgoing::open');
  });

  //
  // We need to close the socket.
  //
  primus.on('outgoing::end', function close() {
    if (socket) {
      socket.onerror = socket.onopen = socket.onclose = socket.onmessage = null;
      socket.close();
      socket = null;
    }
  });
};
<commit_msg>[minor] Remove ws+unix from Faye transformer<commit_after>'use strict';
/*globals MozWebSocket */

/**
 * Minimum viable WebSocket client. This function is stringified and written in
 * to our client side library.
 *
 * @runat client
 * @api private
 */
module.exports = function client() {
  var primus = this
    , socket;

  //
  // Selects an available WebSocket constructor.
  //
  var Factory = (function factory() {
    if ('undefined' !== typeof WebSocket) return WebSocket;
    if ('undefined' !== typeof MozWebSocket) return MozWebSocket;

    try { return Primus.require('faye-websocket').Client; }
    catch (e) {}

    return undefined;
  })();

  if (!Factory) return primus.critical(new Error('Missing required `faye-websocket` module. Please run `npm install --save faye-websocket`'));

  //
  // Connect to the given URL.
  //
  primus.on('outgoing::open', function opening() {
    if (socket) socket.close();

    //
    // FireFox will throw an error when we try to establish a connection from
    // a secure page to an unsecured WebSocket connection. This is inconsistent
    // behaviour between different browsers. This should ideally be solved in
    // Primus when we connect.
    //
    try {
      //
      // Only allow primus.transport object in Node.js, it will throw in
      // browsers with a TypeError if we supply to much arguments.
      //
      if (Factory.length === 3) {
        primus.socket = socket = new Factory(
          primus.uri({ protocol: 'ws', query: true }),  // URL
          [],                                           // Sub protocols
          primus.transport                              // options.
        );
      } else {
        primus.socket = socket = new Factory(primus.uri({ protocol: 'ws', query: true }));
      }
    } catch (e) { return primus.emit('error', e); }

    //
    // Setup the Event handlers.
    //
    socket.binaryType = 'arraybuffer';
    socket.onopen = primus.emits('open');
    socket.onerror = primus.emits('error');
    socket.onclose = primus.emits('end');
    socket.onmessage = primus.emits('data', function parse(evt) {
      return evt.data;
    });
  });

  //
  // We need to write a new message to the socket.
  //
  primus.on('outgoing::data', function write(message) {
    if (!socket || socket.readyState !== 1) return;

    try { socket.send(message); }
    catch (e) { primus.emit('incoming::error', e); }
  });

  //
  // Attempt to reconnect the socket. It assumes that the `outgoing::end` event is
  // called if it failed to disconnect.
  //
  primus.on('outgoing::reconnect', function reconnect() {
    if (socket) primus.emit('outgoing::end');
    primus.emit('outgoing::open');
  });

  //
  // We need to close the socket.
  //
  primus.on('outgoing::end', function close() {
    if (socket) {
      socket.onerror = socket.onopen = socket.onclose = socket.onmessage = null;
      socket.close();
      socket = null;
    }
  });
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

const program = require('commander');
const fse = require('fs-extra');
const path = require('path');
const pkg = require('../package.json');
const utils = require('./utils');
const warning = require('chalk').yellow;
const webpack = require('webpack');
const dotenv = require('dotenv');
const electronBuildPath = 'electron/app';

program
  .name('build-electron.js')
  .option('-e, --env <env>', 'environment', 'dev')
  .option('--release', 'release mode')
  .option('--run', 'debug mode')
  .option('-p, --platform <platform>', 'platform', 'mac')
  .parse(process.argv);

if (!['win', 'appx', 'appx-dev', 'mac', 'mas', 'mas-dev', 'snap'].includes(program.platform)) {
  console.error(`Unsupported Platform: ${program.platform}`);
  process.exit(1);
}

console.log('Start building (webpack)...');

const envFile = `.env.${program.env}`;
console.log(`ENV_FILE: ${warning(envFile)}`);

dotenv.config({ path: envFile });
process.env['ENV_FILE'] = envFile;
process.env['ENV'] = program.env;
process.env['BUILD_TYPE'] = 'electron';
const webpackConfig = require('../webpack.prod');

webpackConfig.plugins.push(
  new webpack.DefinePlugin({
    'process.env.BUILD_TYPE': JSON.stringify('electron'),
    // use mas, appx app for mas-dev and appx-dev build
    'process.env.BUILD_PLATFORM': JSON.stringify(program.platform.release('-dev', '')),
    'process.env.SENTRY_DSN': JSON.stringify(process.env['SENTRY_DSN']),
    'process.env.SENTRY_RELEASE': JSON.stringify(`${pkg.name}.electron-${program.platform}@${pkg.version}`),
  })
);

webpack(webpackConfig, (error, stats) => {
  if (error) throw error;
  if (stats.hasErrors()) {
    console.log(stats.toString({ colors: true }));
    throw new Error('stats errors');
  }

  fse.removeSync(electronBuildPath);
  fse.copySync('build', path.resolve(electronBuildPath), { filter: utils.filterMapFiles });

  if (program.release) {
    // build electron to electron/dist
    console.log(`Start building (electron:${program.platform})...`);
    let { platform } = program;

    if (program.platform === 'mac') {
      platform = 'darwin';
    }
    if (program.platform === 'mas' || program.platform === 'mas-dev') {
      platform = 'mas';
    }
    if (program.platform === 'win' || program.platform === 'appx' || program.platform === 'appx-dev') {
      platform = 'win32';
    }
    if (program.platform === 'snap') {
      platform = 'linux';
    }
    utils.shell(`npm run publish -- --platform=${platform}`, {
      cwd: './electron',
      env: {
        ...process.env,
        BUILD_PLATFORM: program.platform,
      },
    });
    console.log('Electron build Done!');
  } else if (program.run) {
    // run electron app
    console.log(`Debug Electron app building (electron:${program.platform})...`);
    utils.shell('npm start', {
      cwd: './electron',
      env: {
        ...process.env,
        BUILD_PLATFORM: program.platform,
      },
    });
    console.log('Stop electron run!');
  } else {
    console.log('Webpack build Done!');
  }
});
<commit_msg>chore(ci): fix electron build<commit_after>#!/usr/bin/env node
'use strict';

const program = require('commander');
const fse = require('fs-extra');
const path = require('path');
const pkg = require('../package.json');
const utils = require('./utils');
const warning = require('chalk').yellow;
const webpack = require('webpack');
const dotenv = require('dotenv');
const electronBuildPath = 'electron/app';

program
  .name('build-electron.js')
  .option('-e, --env <env>', 'environment', 'dev')
  .option('--release', 'release mode')
  .option('--run', 'debug mode')
  .option('-p, --platform <platform>', 'platform', 'mac')
  .parse(process.argv);

if (!['win', 'appx', 'appx-dev', 'mac', 'mas', 'mas-dev', 'snap'].includes(program.platform)) {
  console.error(`Unsupported Platform: ${program.platform}`);
  process.exit(1);
}

console.log('Start building (webpack)...');

const envFile = `.env.${program.env}`;
console.log(`ENV_FILE: ${warning(envFile)}`);

dotenv.config({ path: envFile });
process.env['ENV_FILE'] = envFile;
process.env['ENV'] = program.env;
process.env['BUILD_TYPE'] = 'electron';
const webpackConfig = require('../webpack.prod');

webpackConfig.plugins.push(
  new webpack.DefinePlugin({
    'process.env.BUILD_TYPE': JSON.stringify('electron'),
    // use mas, appx app for mas-dev and appx-dev build
    'process.env.BUILD_PLATFORM': JSON.stringify(program.platform.replace('-dev', '')),
    'process.env.SENTRY_DSN': JSON.stringify(process.env['SENTRY_DSN']),
    'process.env.SENTRY_RELEASE': JSON.stringify(`${pkg.name}.electron-${program.platform}@${pkg.version}`),
  })
);

webpack(webpackConfig, (error, stats) => {
  if (error) throw error;
  if (stats.hasErrors()) {
    console.log(stats.toString({ colors: true }));
    throw new Error('stats errors');
  }

  fse.removeSync(electronBuildPath);
  fse.copySync('build', path.resolve(electronBuildPath), { filter: utils.filterMapFiles });

  if (program.release) {
    // build electron to electron/dist
    console.log(`Start building (electron:${program.platform})...`);
    let { platform } = program;

    if (program.platform === 'mac') {
      platform = 'darwin';
    }
    if (program.platform === 'mas' || program.platform === 'mas-dev') {
      platform = 'mas';
    }
    if (program.platform === 'win' || program.platform === 'appx' || program.platform === 'appx-dev') {
      platform = 'win32';
    }
    if (program.platform === 'snap') {
      platform = 'linux';
    }
    utils.shell(`npm run publish -- --platform=${platform}`, {
      cwd: './electron',
      env: {
        ...process.env,
        BUILD_PLATFORM: program.platform,
      },
    });
    console.log('Electron build Done!');
  } else if (program.run) {
    // run electron app
    console.log(`Debug Electron app building (electron:${program.platform})...`);
    utils.shell('npm start', {
      cwd: './electron',
      env: {
        ...process.env,
        BUILD_PLATFORM: program.platform,
      },
    });
    console.log('Stop electron run!');
  } else {
    console.log('Webpack build Done!');
  }
});
<|endoftext|>


<commit_before>var fs = require('fs'),
	_ = require("lodash"),
	fetch = require('node-fetch'),
	request = require('request'),
	parseString = require('xml2js').parseString,
	Indexer = require('./indexer');

let cardIndexer = new Indexer(
  "http://localhost:9200",
  "cards", "card"
);

var setNamesToChange = {tuples:[["Limited Edition Alpha","Alpha Edition"],
						["Limited Edition Beta","Beta Edition"],
						["Seventh Edition","7th Edition"],
						["Eighth Edition","8th Edition"],
						["Ninth Edition","9th Edition"],
						["Ravnica: City of Guilds","Ravnica"],
						["Tenth Edition","10th Edition"],
						["Time Spiral \"Timeshifted\"", "Timeshifted"],
						["Magic 2010","Magic 2010 (M10)"],
						["Magic 2011","Magic 2011 (M11)"],
						["Magic: The Gathering-Commander","Commander"],
						["Magic 2012","Magic 2012 (M12)"],
						["Planechase 2012 Edition","Planechase 2012"],
						["Magic 2013","Magic 2013 (M13)"],
						["Magic 2014 Core Set","Magic 2014 (M14)"],
						["Commander 2013 Edition","Commander 2013"],
						["Duel Decks Anthology, Divine vs. Demonic","Duel Decks: Anthology"],
						["Duel Decks Anthology, Elves vs. Goblins","Duel Decks: Anthology"],
						["Duel Decks Anthology, Garruk vs. Liliana","Duel Decks: Anthology"],
						["Duel Decks Anthology, Jace vs. Chandra","Duel Decks: Anthology"],
						["Magic 2015 Core Set","Magic 2015 (M15)"],
						["Modern Masters 2015 Edition", "Modern Masters 2015"],
						["Magic: The Gathering—Conspiracy","Conspiracy"],
						["From the Vault: Annihilation (2014)", "From the Vault: Annihilation"],
						["Media Inserts","Media Promos"],
						["Modern Event Deck 2014","Magic Modern Event Deck"]]};

function checkForSetNameReplacement(setName) {
	for (var pair = 0; pair < setNamesToChange.tuples.length; pair++) {
		if (setName == setNamesToChange.tuples[pair][0]) {
			console.log("replacing " + setName + " with " + setNamesToChange.tuples[pair][1]);
			setName = setNamesToChange.tuples[pair][1];
			break;
		}
	}
	return setName;
}

// Don't forget; From The Vault cards ONLY HAVE FOIL PRICES. Probably other promos too.

function getESData2() {
	console.log("about to fetch");
	return new Promise(function(resolve, reject) {
		fetch('http://localhost:9200/cards/card/_search?from=0&size=5000')
		.then(function(res) {
	        resolve( res.json());
	    })
	})
}

function getTCGPlayerPrices(doc) {
	for (var multiId = 0; multiId < doc._source.multiverseids.length; multiId++) { // Loop over all editions in the doc.
		var setName = doc._source.multiverseids[multiId].setName;
		setName = checkForSetNameReplacement(setName); // Ensure the set name is TCGPlayer compatible.
		var priceUrl = "http://partner.tcgplayer.com/x3/phl.asmx/p?pk=TCGTEST&s="+setName+"&p="+doc._source.name;
		requestPrices(doc._source.multiverseids[multiId], priceUrl).then(function(result) {
			doc._source.multiverseids[multiId] = result;
			console.log(doc._source.multiverseids[multiId]);
		});
	}
	//console.log('doc id: ' + doc._id);
}

function requestPrices(multiIdObject, priceUrl) {
	//console.log("requesting priceUrl " + priceUrl);
	return new Promise(function(resolve) {
		request(priceUrl, function(error, response, body) {
		  	if (!error && response.statusCode == 200 ) {
		  		parseString(body, function (err, result) {
		  			if (result.products.product !== undefined) { // Safeguard against weird results.
			  			//console.log(result);
			    		multiIdObject.lowPrice = parseFloat(result.products.product[0].lowprice[0]);
			    		//console.log("written " + result.products.product[0].lowprice[0] + ' to ' + multiIdObject.lowPrice );
			    		multiIdObject.medPrice = parseFloat(result.products.product[0].avgprice[0]);
			    		//console.log("written " + result.products.product[0].avgprice[0] + ' to ' + multiIdObject.medPrice );
			    		multiIdObject.hiPrice = parseFloat(result.products.product[0].hiprice[0]);
			    		//console.log("written " + result.products.product[0].hiprice[0] + ' to ' + multiIdObject.hiPrice );
			    		multiIdObject.foilPrice = parseFloat(result.products.product[0].foilavgprice[0]);
			    		//console.log("written " + result.products.product[0].foilavgprice[0] + ' to ' + multiIdObject.foilPrice );
			    		multiIdObject.storeLink = result.products.product[0].link[0];
			    		//console.log("written " + result.products.product[0].link[0] + ' to ' + multiIdObject.storeLink );
			    	}
				});
		  	}
		  	else {
		  		console.log(body + ' with link ' + priceUrl); // Product not found.
		  	}
			resolve(multiIdObject);
		  	//console.log(thisMultiId);
		});
	});
}

async function printDocs(){
  // "await" resolution or rejection of the promise
  // use try/catch for error handling
  try {
    var docs = await getESData2();
    //console.log(docs);
    var startTime = Date.now();
    // now you can write this like syncronous code!
    for (var hit = 0; hit < docs.hits.hits.length; hit++) {
      //console.log('\n+++++++++++'+docs.hits.hits[hit]._id);
      for (var edition = 0; edition < docs.hits.hits[hit]._source.multiverseids.length; edition++ ) {
      	//console.log(docs.hits.hits[hit]._source.multiverseids[edition]);
      	var setName = docs.hits.hits[hit]._source.multiverseids[edition].setName;
		setName = checkForSetNameReplacement(setName); // Ensure the set name is TCGPlayer compatible.
		// Have to change fuse/split card names to reflect both prices.
		var name = docs.hits.hits[hit]._source.name;
		if (docs.hits.hits[hit]._source.layout == "split") {
			name = docs.hits.hits[hit]._source.names[0] + ' // ' + docs.hits.hits[hit]._source.names[1];
		}
		// If it's a DFC, use the original side's name.
		else if ((docs.hits.hits[hit]._source.layout == "double-faced" || docs.hits.hits[hit]._source.layout == "flip")
					&& docs.hits.hits[hit]._source.name == docs.hits.hits[hit]._source.names[1]) {
			name = docs.hits.hits[hit]._source.names[0];
		}
		// If it's a token, append "Token" to the name.
		else if (docs.hits.hits[hit]._source.layout == "token") {
			name = name + " Token";
		}
		// Remove all "" from the name (like Ach, Hans Run and Kongming, Sleeping Dragon)
		name = name.replace(/"/g,"");
		var priceUrl = "http://partner.tcgplayer.com/x3/phl.asmx/p?pk=MTGHUNTER&s="+setName+"&p="+name;
		docs.hits.hits[hit]._source.multiverseids[edition] = await requestPrices(docs.hits.hits[hit]._source.multiverseids[edition], priceUrl);
      }
      //console.log('\n====='+JSON.stringify(docs.hits.hits[hit]._source));
      // Now send this modified data back to the ES server with an update push.
      cardIndexer.updateSingleDocument(docs.hits.hits[hit]);
      console.log("---done " + docs.hits.hits[hit]._source.name + ". elapsed time: " + (Date.now() - startTime) / 1000);
    }

	//console.log(JSON.stringify(docs));
  } catch (e) {
    // promise was rejected and we can handle errors with try/catch!
  }
}

function main2() {
	printDocs();
}

main2();

//);<commit_msg>Small updates to price getter.<commit_after>var fs = require('fs'),
	_ = require("lodash"),
	path = require('path'),
	fetch = require('node-fetch'),
	request = require('request'),
	parseString = require('xml2js').parseString,
	Indexer = require('./indexer');

let cardIndexer = new Indexer(
  "http://localhost:9200",
  "cards", "card"
);

var failedRequests = [];

var setNamesToChange = {tuples:[["Limited Edition Alpha","Alpha Edition"],
						["Limited Edition Beta","Beta Edition"],
						["Seventh Edition","7th Edition"],
						["Eighth Edition","8th Edition"],
						["Ninth Edition","9th Edition"],
						["Ravnica: City of Guilds","Ravnica"],
						["Tenth Edition","10th Edition"],
						["Time Spiral \"Timeshifted\"", "Timeshifted"],
						["Magic 2010","Magic 2010 (M10)"],
						["Magic 2011","Magic 2011 (M11)"],
						["Magic: The Gathering-Commander","Commander"],
						["Magic 2012","Magic 2012 (M12)"],
						["Planechase 2012 Edition","Planechase 2012"],
						["Magic 2013","Magic 2013 (M13)"],
						["Magic 2014 Core Set","Magic 2014 (M14)"],
						["Commander 2013 Edition","Commander 2013"],
						["Duel Decks Anthology, Divine vs. Demonic","Duel Decks: Anthology"],
						["Duel Decks Anthology, Elves vs. Goblins","Duel Decks: Anthology"],
						["Duel Decks Anthology, Garruk vs. Liliana","Duel Decks: Anthology"],
						["Duel Decks Anthology, Jace vs. Chandra","Duel Decks: Anthology"],
						["Magic 2015 Core Set","Magic 2015 (M15)"],
						["Modern Masters 2015 Edition", "Modern Masters 2015"],
						["Magic: The Gathering—Conspiracy","Conspiracy"],
						["From the Vault: Annihilation (2014)", "From the Vault: Annihilation"],
						["Media Inserts","Media Promos"],
						["Modern Event Deck 2014","Magic Modern Event Deck"]]};

function checkForSetNameReplacement(setName) {
	for (var pair = 0; pair < setNamesToChange.tuples.length; pair++) {
		if (setName == setNamesToChange.tuples[pair][0]) {
			console.log("replacing " + setName + " with " + setNamesToChange.tuples[pair][1]);
			setName = setNamesToChange.tuples[pair][1];
			break;
		}
	}
	return setName;
}

// Don't forget; From The Vault cards ONLY HAVE FOIL PRICES. Probably other promos too.

function getESData2(isSecondRun) {
	console.log("about to fetch");
	if (isSecondRun) {
		return new Promise(function(resolve, reject) {
			fetch('http://localhost:9200/cards/card/_search?from=0&size=10000')
			.then(function(res) {
		        resolve( res.json() );
		    })
		})
	}
	else {
		return new Promise(function(resolve, reject) {
			fetch('http://localhost:9200/cards/card/_search?from=10000&size=10000')
			.then(function(res) {
		        resolve( res.json() );
		    })
		})
	}
}

function getTCGPlayerPrices(doc) {
	for (var multiId = 0; multiId < doc._source.multiverseids.length; multiId++) { // Loop over all editions in the doc.
		var setName = doc._source.multiverseids[multiId].setName;
		setName = checkForSetNameReplacement(setName); // Ensure the set name is TCGPlayer compatible.
		var priceUrl = "http://partner.tcgplayer.com/x3/phl.asmx/p?pk=TCGTEST&s="+setName+"&p="+doc._source.name;
		requestPrices(doc._source.multiverseids[multiId], priceUrl).then(function(result) {
			doc._source.multiverseids[multiId] = result;
			console.log(doc._source.multiverseids[multiId]);
		});
	}
	//console.log('doc id: ' + doc._id);
}

function requestPrices(multiIdObject, priceUrl) {
	//console.log("requesting priceUrl " + priceUrl);
	return new Promise(function(resolve) {
		request(priceUrl, function(error, response, body) {
		  	if (!error && response.statusCode == 200 ) {
		  		parseString(body, function (err, result) {
		  			if (result.products.product !== undefined) { // Safeguard against weird results.
			  			//console.log(result);
			    		multiIdObject.lowPrice = parseFloat(result.products.product[0].lowprice[0]);
			    		//console.log("written " + result.products.product[0].lowprice[0] + ' to ' + multiIdObject.lowPrice );
			    		multiIdObject.medPrice = parseFloat(result.products.product[0].avgprice[0]);
			    		//console.log("written " + result.products.product[0].avgprice[0] + ' to ' + multiIdObject.medPrice );
			    		multiIdObject.hiPrice = parseFloat(result.products.product[0].hiprice[0]);
			    		//console.log("written " + result.products.product[0].hiprice[0] + ' to ' + multiIdObject.hiPrice );
			    		multiIdObject.foilPrice = parseFloat(result.products.product[0].foilavgprice[0]);
			    		//console.log("written " + result.products.product[0].foilavgprice[0] + ' to ' + multiIdObject.foilPrice );
			    		multiIdObject.storeLink = result.products.product[0].link[0];
			    		//console.log("written " + result.products.product[0].link[0] + ' to ' + multiIdObject.storeLink );
			    	}
				});
		  	}
		  	else {
		  		console.log(body + ' with link ' + priceUrl); // Product not found.
		  		failedRequests.push(priceUrl+'\n');
		  		//console.log(failedRequests);
		  	}
			resolve(multiIdObject);
		  	//console.log(thisMultiId);
		});
	});
}

async function printDocs(isSecondRun){
  // "await" resolution or rejection of the promise
  // use try/catch for error handling
    try {
	    var docs = await getESData2();
	    //console.log(docs);
	    var startTime = Date.now();
	    // now you can write this like syncronous code!
	    for (var hit = 0; hit < docs.hits.hits.length; hit++) {
	        //console.log('\n+++++++++++'+docs.hits.hits[hit]._id);
	        for (var edition = 0; edition < docs.hits.hits[hit]._source.multiverseids.length; edition++ ) {
		      	//console.log(docs.hits.hits[hit]._source.multiverseids[edition]);
		      	var setName = docs.hits.hits[hit]._source.multiverseids[edition].setName;
				setName = checkForSetNameReplacement(setName); // Ensure the set name is TCGPlayer compatible.
				// Have to change fuse/split card names to reflect both prices.
				var name = docs.hits.hits[hit]._source.name;
				if (docs.hits.hits[hit]._source.layout == "split") {
					name = docs.hits.hits[hit]._source.names[0] + ' // ' + docs.hits.hits[hit]._source.names[1];
				}
				// If it's a DFC, use the original side's name.
				else if ((docs.hits.hits[hit]._source.layout == "double-faced" || docs.hits.hits[hit]._source.layout == "flip")
							&& docs.hits.hits[hit]._source.name == docs.hits.hits[hit]._source.names[1]) {
					name = docs.hits.hits[hit]._source.names[0];
				}
				// If it's a token, append "Token" to the name.
				else if (docs.hits.hits[hit]._source.layout == "token") {
					name = name + " Token";
				}
				// Remove all "" from the name (like Ach, Hans Run and Kongming, Sleeping Dragon)
				name = name.replace(/"/g,"");
				var priceUrl = "http://partner.tcgplayer.com/x3/phl.asmx/p?pk=MTGHUNTER&s="+setName+"&p="+name;
				docs.hits.hits[hit]._source.multiverseids[edition] = await requestPrices(docs.hits.hits[hit]._source.multiverseids[edition], priceUrl);
	        }
	    	//console.log('\n====='+JSON.stringify(docs.hits.hits[hit]._source));
	    	// Now send this modified data back to the ES server with an update push.
	    	cardIndexer.updateSingleDocument(docs.hits.hits[hit]);
	    	console.log("---done " + docs.hits.hits[hit]._source.name + ". elapsed time: " + (Date.now() - startTime) / 1000);
    	}
		//console.log(JSON.stringify(docs));
    } catch (e) {
	    // promise was rejected and we can handle errors with try/catch!
    }
}

async function main2() {
	await printDocs(false); // False means first run.
	console.log("FINISHED RUN 1.");
	//await printDocs(true); // True means second run.
	//console.log("FINISHED RUN 2. writing errors to " + __dirname);
	fs.writeFile(path.join(__dirname, 'failedRequests.json'), JSON.stringify(failedRequests, null, '  '), 'utf8', this);
}

function launcher() {
	main2();
}

launcher();

//);<|endoftext|>


<commit_before>"use strict";

const $ = require("jquery");
const Mousetrap = require("mousetrap");
const input = $("#input");
const sidebar = $("#sidebar");
const windows = $("#windows");
const contextMenuContainer = $("#context-menu-container");

Mousetrap.bind([
	"pageup",
	"pagedown",
], function(e, key) {
	let container = windows.find(".window.active");

	// Chat windows scroll message container
	if (container.attr("id") === "chat-container") {
		container = container.find(".chan.active .chat");
	}

	container.finish();

	const offset = container.get(0).clientHeight * 0.9;
	let scrollTop = container.scrollTop();

	if (key === "pageup") {
		scrollTop = Math.floor(scrollTop - offset);
	} else {
		scrollTop = Math.ceil(scrollTop + offset);
	}

	container.animate({
		scrollTop: scrollTop,
	}, 200);

	return false;
});

Mousetrap.bind([
	"command+up",
	"command+down",
	"ctrl+up",
	"ctrl+down",
], function(e, keys) {
	const channels = sidebar.find(".chan");
	const index = channels.index(channels.filter(".active"));
	const direction = keys.split("+").pop();
	let target;

	switch (direction) {
	case "up":
		target = (channels.length + (index - 1 + channels.length)) % channels.length;
		break;

	case "down":
		target = (channels.length + (index + 1 + channels.length)) % channels.length;
		break;
	}

	channels.eq(target).click();
});

Mousetrap.bind([
	"escape",
], function() {
	contextMenuContainer.hide();
});

const colorsHotkeys = {
	k: "\x03",
	b: "\x02",
	u: "\x1F",
	i: "\x1D",
	o: "\x0F",
	s: "\x1e",
	m: "\x11",
};

for (const hotkey in colorsHotkeys) {
	Mousetrap.bind([
		"command+" + hotkey,
		"ctrl+" + hotkey,
	], function(e) {
		e.preventDefault();

		const cursorPosStart = input.prop("selectionStart");
		const cursorPosEnd = input.prop("selectionEnd");
		const value = input.val();
		let newValue = value.substring(0, cursorPosStart) + colorsHotkeys[e.key];

		if (cursorPosStart === cursorPosEnd) {
			// If no text is selected, insert at cursor
			newValue += value.substring(cursorPosEnd, value.length);
		} else {
			// If text is selected, insert formatting character at start and the end
			newValue += value.substring(cursorPosStart, cursorPosEnd) + colorsHotkeys[e.key] + value.substring(cursorPosEnd, value.length);
		}

		input
			.val(newValue)
			.get(0).setSelectionRange(cursorPosStart + 1, cursorPosEnd + 1);
	});
}
<commit_msg>Use cross-platform modifier shortcut for Mousetrap when possible<commit_after>"use strict";

const $ = require("jquery");
const Mousetrap = require("mousetrap");
const input = $("#input");
const sidebar = $("#sidebar");
const windows = $("#windows");
const contextMenuContainer = $("#context-menu-container");

Mousetrap.bind([
	"pageup",
	"pagedown",
], function(e, key) {
	let container = windows.find(".window.active");

	// Chat windows scroll message container
	if (container.attr("id") === "chat-container") {
		container = container.find(".chan.active .chat");
	}

	container.finish();

	const offset = container.get(0).clientHeight * 0.9;
	let scrollTop = container.scrollTop();

	if (key === "pageup") {
		scrollTop = Math.floor(scrollTop - offset);
	} else {
		scrollTop = Math.ceil(scrollTop + offset);
	}

	container.animate({
		scrollTop: scrollTop,
	}, 200);

	return false;
});

Mousetrap.bind([
	"mod+up",
	"mod+down",
], function(e, keys) {
	const channels = sidebar.find(".chan");
	const index = channels.index(channels.filter(".active"));
	const direction = keys.split("+").pop();
	let target;

	switch (direction) {
	case "up":
		target = (channels.length + (index - 1 + channels.length)) % channels.length;
		break;

	case "down":
		target = (channels.length + (index + 1 + channels.length)) % channels.length;
		break;
	}

	channels.eq(target).click();
});

Mousetrap.bind([
	"escape",
], function() {
	contextMenuContainer.hide();
});

const colorsHotkeys = {
	k: "\x03",
	b: "\x02",
	u: "\x1F",
	i: "\x1D",
	o: "\x0F",
	s: "\x1e",
	m: "\x11",
};

for (const hotkey in colorsHotkeys) {
	Mousetrap.bind("mod+" + hotkey, function(e) {
		e.preventDefault();

		const cursorPosStart = input.prop("selectionStart");
		const cursorPosEnd = input.prop("selectionEnd");
		const value = input.val();
		let newValue = value.substring(0, cursorPosStart) + colorsHotkeys[e.key];

		if (cursorPosStart === cursorPosEnd) {
			// If no text is selected, insert at cursor
			newValue += value.substring(cursorPosEnd, value.length);
		} else {
			// If text is selected, insert formatting character at start and the end
			newValue += value.substring(cursorPosStart, cursorPosEnd) + colorsHotkeys[e.key] + value.substring(cursorPosEnd, value.length);
		}

		input
			.val(newValue)
			.get(0).setSelectionRange(cursorPosStart + 1, cursorPosEnd + 1);
	});
}
<|endoftext|>


<commit_before>require('dotenv/config')
const { DefinePlugin } = require('webpack')

module.exports = {
  webpack: config => {
    config.plugins = config.plugins
      .filter(plugin =>
        process.env.NODE_ENV === 'production' ||
        plugin.constructor.name !== 'UglifyJsPlugin'
      )
      .concat(new DefinePlugin({
        'process.env.API_URL': JSON.stringify(process.env.API_URL)
      }))
    return config
  }
}
<commit_msg>Move reliance on NODE_ENV from next config. #35<commit_after>require('dotenv/config')
const { DefinePlugin } = require('webpack')

module.exports = {
  webpack: (config, { dev }) => {
    config.plugins = config.plugins
      .filter(plugin => dev
        ? plugin.constructor.name !== 'UglifyJsPlugin'
        : true
      )
      .concat(new DefinePlugin({
        'process.env.API_URL': JSON.stringify(process.env.API_URL),
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }))
    return config
  }
}
<|endoftext|>


<commit_before>'use strict';

var aFrom          = require('es5-ext/array/from')
  , compact        = require('es5-ext/array/#/compact')
  , isCopy         = require('es5-ext/array/#/is-copy')
  , pluck          = require('es5-ext/function/pluck')
  , copy           = require('es5-ext/object/copy')
  , assign         = require('es5-ext/object/assign')
  , isObject       = require('es5-ext/object/is-object')
  , callable       = require('es5-ext/object/valid-callable')
  , toArray        = require('es5-ext/array/to-array')
  , isMap          = require('es6-map/is-map')
  , d              = require('d')
  , memoize        = require('memoizee/plain')
  , memoizeMethods = require('memoizee/methods-plain')
  , getNormalizer  = require('memoizee/normalizers/get-1')
  , makeEl         = require('dom-ext/document/#/make-element')
  , append         = require('dom-ext/element/#/append')
  , clear          = require('dom-ext/element/#/clear')
  , remove         = require('dom-ext/element/#/remove')
  , replaceCont    = require('dom-ext/element/#/replace-content')
  , dispatchEvnt   = require('dom-ext/html-element/#/dispatch-event-2')
  , resolveOptions = require('../utils/resolve-options')
  , DOMInput       = require('../_controls/input')
  , eventOpts      = require('../_event-options')
  , setup          = require('../')

  , getMapValue = pluck(1)
  , isArray = Array.isArray, map = Array.prototype.map
  , defineProperty = Object.defineProperty
  , defineProperties = Object.defineProperties
  , getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
  , Input, render, renderItem, getTempForm, getForceReset;

getForceReset = memoize(function (document) {
	var form = document.createElement('form');
	return function (input) {
		var parent = input.parentNode, nextSibling = input.nextSibling;
		form.appendChild(input);
		form.reset();
		parent.insertBefore(input, nextSibling);
	};
}, { normalizer: getNormalizer() });

getTempForm = memoize(function (document) {
	var form = document.createElement('form');
	form.setAttribute('method', 'post');
	form.setAttribute('enctype', 'multipart/form-data');
	form.setAttribute('style', 'display: none');
	return form;
}, { normalizer: getNormalizer() });

render = function (options) {
	var el = this.make;
	return el('div', { class: 'dbjs-file' },
		this.valueDOM = this.multiple ? el('ul') : el('span'),
		el('label', { class: 'dbjs-file-input' }, options.label,
			this.control = el('input', { type: 'file' })));
};

renderItem = function (file) {
	var el = this.make, data = {};
	data.dom = el(this.multiple ? 'li' : 'span', { 'data-id': file.__id__ });
	append.call(data.dom,
		el('a', { href: file._url, target: '_blank' }, file._name), " ",
		data.control = el('input', { type: 'hidden', name: this.name,
			value: file.__id__ }),
		el('a', { class: 'clear', onclick: this.removeItem.bind(this, data.dom) },
			"x"));
	return data;
};

var byNameLastModified = function (f1, f2) {
	f1 = this.getById(f1);
	if (f1) f1 = f1.getDescriptor('name')._lastOwnModified_;
	f2 = this.getById(f2);
	if (f2) f2 = f2.getDescriptor('name')._lastOwnModified_;
	return (f1 || Infinity) - (f2 || Infinity);
};

Input = function (document, type/*, options*/) {
	var options = arguments[2], action;
	this.make = makeEl.bind(document);
	this.controls = [];
	this.type = type;
	options = resolveOptions(options, type);
	if (options.multiple) this.multiple = true;
	((options.render == null) || callable(options.render));
	defineProperty(this, 'renderItem', d((options.renderItem == null)
		? renderItem : callable(options.renderItem)));
	DOMInput.call(this, document, type, options);
	if (this.multiple) this.control.setAttribute('multiple', 'multiple');
	this.control.setAttribute('accept', toArray(type.accept).join(','));

	if (options.autoSubmit != null) {
		action = options.autoSubmit;
		this.on('change', function () {
			var form, parent, sibling;
			if (!this.changed) return;
			form = getTempForm(document);
			parent = this.dom.parentNode;
			sibling = this.dom.nextSibling;
			form.setAttribute('action', action);
			document.body.appendChild(form);
			form.appendChild(this.dom);
			dispatchEvnt.call(form, 'submit');
			parent.insertBefore(this.dom, sibling);
			if (form.parentNode) form.parentNode.removeChild(form);
		});
	}
};

Input.prototype = Object.create(DOMInput.prototype, assign({
	constructor: d(Input),
	multiple: d(false),
	_value: d(null),
	controlAttributes: d(assign(copy(DOMInput.prototype.controlAttributes),
		{ required: true })),
	dbAttributes: d(assign(copy(DOMInput.prototype.dbAttributes),
		{ required: true })),
	_render: d(function (options) {
		this.dom = (options.render || render).call(this, options);
		this.controls.push(this.control);
	}),
	name: d.gs(getName, function (name) {
		this._name = name;
		name = this.name;
		this.controls.forEach(function (input) { input.name = name; });
	}),
	inputValue: d.gs(function () {
		var value;
		if (!this.multiple) {
			if (this.control.files && this.control.files[0]) return this.control.files[0];
			if (!this.valueDOM.firstElementChild) return null;
			return this.valueDOM.firstElementChild.getAttribute('data-id');
		}
		value = map.call(this.valueDOM.childNodes, function (node) {
			var id = node.getAttribute('data-id');
			if (!id) {
				throw new TypeError("Missing id (data-id attribute) on file item");
			}
			return id;
		}).concat(this.control.files ? aFrom(this.control.files) : []);
		return value.length ? value : null;
	}, function (nu) {
		var old = this.inputValue, changed;
		this._value = nu;
		if (this.multiple) {
			if (nu) {
				if (!old || !isCopy.call(nu, old)) {
					this.control.value = null;
					if (this.control.files && this.control.files.length) {
						// In Opera control is not reset properly, force it with hack
						getForceReset(this.document)(this.control);
					}
					replaceCont.call(this.valueDOM,
						nu.sort(byNameLastModified.bind(this.type)).map(this._renderItem));
					if (this._required) this.castControlAttribute('required', false);
					changed = true;
				}
			} else {
				clear.call(this.valueDOM);
				if (this._required) this.castControlAttribute('required', true);
			}
		} else if (nu !== old) {
			this.control.value = null;
			if (nu) {
				replaceCont.call(this.valueDOM, this._renderItem(nu));
				if (this._required) this.castControlAttribute('required', false);
				this.dom.classList.add('filled');
			} else {
				clear.call(this.valueDOM);
				if (this._required) this.castControlAttribute('required', true);
				this.dom.classList.remove('filled');
			}
			changed = true;
		}
		if (changed) {
			try {
				dispatchEvnt.call(this.control, 'change', eventOpts);
			} catch (ignore) {}
		} else {
			this.onChange();
		}
	}),
	value: d.gs(function () {
		var value = this.inputValue;
		if (value == null) return null;
		if (this.multiple) return value.map(this.type.toInputValue, this.type);
		return this.type.toInputValue(value);
	}, function (value) {
		if (value == null) {
			value = null;
		} else if (this.multiple) {
			if (isMap(value)) value = toArray(value).map(getMapValue);
			else value = toArray(value);
			if (value.length) {
				value = compact.call(value.map(this.type.toInputValue, this.type));
			}
			if (!value.length) value = null;
		} else {
			value = this.type.toInputValue(value);
		}

		this.inputValue = value;
	}),
	onChange: d(function () {
		var value, changed, valid, emitChanged, emitValid;
		value = this.inputValue;
		changed = (this.multiple && (this._value != null) && (value != null))
			? isCopy.call(value, this._value) : (value !== this._value);
		valid = this.required ? (value != null) : true;

		if (this.changed !== changed) {
			this.changed = changed;
			emitChanged = true;
		}
		if (this.valid !== valid) {
			this.valid = valid;
			emitValid = true;
		}

		this.emit('change', value);
		if (emitChanged) this.emit('change:changed', this.changed);
		if (emitValid) this.emit('change:valid', this.valid);
	}),
	removeItem: d(function (dom) {
		remove.call(dom);
		if (!this.multiple) this.dom.classList.remove('filled');
		dispatchEvnt.call(this.control, 'change', eventOpts);
	})
}, memoizeMethods({
	_renderItem: d(function (file) {
		var data;
		file = this.type.getById(file);
		data = this.renderItem(file);
		if (data.control) this.controls.push(data.control);
		return data.dom;
	})
})));

module.exports = memoize(function (db) {
	defineProperties(setup(db).File, {
		fromInputValue: d(function (value) {
			if (value == null) return null;
			if (value === '') return null;
			if (isArray(value) && (value.length === 2)) {
				if (typeof value[0] === 'string') {
					if (isObject(value[1])) return value[1];
				} else if (typeof value[1] === 'string') {
					if (isObject(value[0])) return value[0];
				}
				return value;
			}
			if (isObject(value)) return value;
			value = value.trim();
			if (!value) return null;
			return this.getById(value);
		}),
		DOMInput: d(Input),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple || (options.type !== 'checkbox')) {
				return new this.DOMInput(document, this, options);
			}
			return db.Object.toDOMInput(document, options);
		})
	});
}, { normalizer: getNormalizer() });

exports.Input = Input;
<commit_msg>Checkbox based removal<commit_after>'use strict';

var aFrom          = require('es5-ext/array/from')
  , compact        = require('es5-ext/array/#/compact')
  , isCopy         = require('es5-ext/array/#/is-copy')
  , pluck          = require('es5-ext/function/pluck')
  , copy           = require('es5-ext/object/copy')
  , assign         = require('es5-ext/object/assign')
  , isObject       = require('es5-ext/object/is-object')
  , callable       = require('es5-ext/object/valid-callable')
  , toArray        = require('es5-ext/array/to-array')
  , isMap          = require('es6-map/is-map')
  , d              = require('d')
  , memoize        = require('memoizee/plain')
  , memoizeMethods = require('memoizee/methods-plain')
  , getNormalizer  = require('memoizee/normalizers/get-1')
  , makeEl         = require('dom-ext/document/#/make-element')
  , append         = require('dom-ext/element/#/append')
  , clear          = require('dom-ext/element/#/clear')
  , remove         = require('dom-ext/element/#/remove')
  , replaceCont    = require('dom-ext/element/#/replace-content')
  , dispatchEvnt   = require('dom-ext/html-element/#/dispatch-event-2')
  , isNested       = require('dbjs/is-dbjs-nested-object')
  , resolveOptions = require('../utils/resolve-options')
  , DOMInput       = require('../_controls/input')
  , eventOpts      = require('../_event-options')
  , setup          = require('../')

  , getMapValue = pluck(1)
  , isArray = Array.isArray, map = Array.prototype.map
  , defineProperty = Object.defineProperty
  , defineProperties = Object.defineProperties
  , getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
  , Input, render, renderItem, getTempForm, getForceReset;

getForceReset = memoize(function (document) {
	var form = document.createElement('form');
	return function (input) {
		var parent = input.parentNode, nextSibling = input.nextSibling;
		form.appendChild(input);
		form.reset();
		parent.insertBefore(input, nextSibling);
	};
}, { normalizer: getNormalizer() });

getTempForm = memoize(function (document) {
	var form = document.createElement('form');
	form.setAttribute('method', 'post');
	form.setAttribute('enctype', 'multipart/form-data');
	form.setAttribute('style', 'display: none');
	return form;
}, { normalizer: getNormalizer() });

render = function (options) {
	var el = this.make;
	return el('div', { class: 'dbjs-file' },
		this.valueDOM = this.multiple ? el('ul') : el('span'),
		el('label', { class: 'dbjs-file-input' }, options.label,
			this.control = el('input', { type: 'file' })));
};

renderItem = function (file) {
	var el = this.make, data = {}, name;
	data.dom = el(this.multiple ? 'li' : 'span', { 'data-id': file.__id__ });
	if (isNested(file)) name = file.__id__;
	else name = this.descriptor.__valueId__ + '*' + file.__id__;
	append.call(data.dom,
		el('a', { href: file._url, target: '_blank' }, file._name), " ",
		el('input', { class: 'clear' },
			el('input', { type: 'checkbox', name: name, value: '' }), "x"));
	return data;
};

var byNameLastModified = function (f1, f2) {
	f1 = this.getById(f1);
	if (f1) f1 = f1.getDescriptor('name')._lastOwnModified_;
	f2 = this.getById(f2);
	if (f2) f2 = f2.getDescriptor('name')._lastOwnModified_;
	return (f1 || Infinity) - (f2 || Infinity);
};

Input = function (document, type/*, options*/) {
	var options = arguments[2], action;
	this.make = makeEl.bind(document);
	this.controls = [];
	this.type = type;
	options = resolveOptions(options, type);
	if (options.multiple) this.multiple = true;
	((options.render == null) || callable(options.render));
	defineProperty(this, 'renderItem', d((options.renderItem == null)
		? renderItem : callable(options.renderItem)));
	DOMInput.call(this, document, type, options);
	if (this.multiple) this.control.setAttribute('multiple', 'multiple');
	this.control.setAttribute('accept', toArray(type.accept).join(','));

	if (options.autoSubmit != null) {
		action = options.autoSubmit;
		this.on('change', function () {
			var form, parent, sibling;
			if (!this.changed) return;
			form = getTempForm(document);
			parent = this.dom.parentNode;
			sibling = this.dom.nextSibling;
			form.setAttribute('action', action);
			document.body.appendChild(form);
			form.appendChild(this.dom);
			dispatchEvnt.call(form, 'submit');
			parent.insertBefore(this.dom, sibling);
			if (form.parentNode) form.parentNode.removeChild(form);
		});
	}
};

Input.prototype = Object.create(DOMInput.prototype, assign({
	constructor: d(Input),
	multiple: d(false),
	_value: d(null),
	controlAttributes: d(assign(copy(DOMInput.prototype.controlAttributes),
		{ required: true })),
	dbAttributes: d(assign(copy(DOMInput.prototype.dbAttributes),
		{ required: true })),
	_render: d(function (options) {
		this.dom = (options.render || render).call(this, options);
		this.controls.push(this.control);
	}),
	name: d.gs(getName, function (name) {
		this._name = name;
		name = this.name;
		this.controls.forEach(function (input) { input.name = name; });
	}),
	inputValue: d.gs(function () {
		var value;
		if (!this.multiple) {
			if (this.control.files && this.control.files[0]) return this.control.files[0];
			if (!this.valueDOM.firstElementChild) return null;
			return this.valueDOM.firstElementChild.getAttribute('data-id');
		}
		value = map.call(this.valueDOM.childNodes, function (node) {
			var id = node.getAttribute('data-id');
			if (!id) {
				throw new TypeError("Missing id (data-id attribute) on file item");
			}
			return id;
		}).concat(this.control.files ? aFrom(this.control.files) : []);
		return value.length ? value : null;
	}, function (nu) {
		var old = this.inputValue, changed;
		this._value = nu;
		if (this.multiple) {
			if (nu) {
				if (!old || !isCopy.call(nu, old)) {
					this.control.value = null;
					if (this.control.files && this.control.files.length) {
						// In Opera control is not reset properly, force it with hack
						getForceReset(this.document)(this.control);
					}
					replaceCont.call(this.valueDOM,
						nu.sort(byNameLastModified.bind(this.type)).map(this._renderItem));
					if (this._required) this.castControlAttribute('required', false);
					changed = true;
				}
			} else {
				clear.call(this.valueDOM);
				if (this._required) this.castControlAttribute('required', true);
			}
		} else if (nu !== old) {
			this.control.value = null;
			if (nu) {
				replaceCont.call(this.valueDOM, this._renderItem(nu));
				if (this._required) this.castControlAttribute('required', false);
				this.dom.classList.add('filled');
			} else {
				clear.call(this.valueDOM);
				if (this._required) this.castControlAttribute('required', true);
				this.dom.classList.remove('filled');
			}
			changed = true;
		}
		if (changed) {
			try {
				dispatchEvnt.call(this.control, 'change', eventOpts);
			} catch (ignore) {}
		} else {
			this.onChange();
		}
	}),
	value: d.gs(function () {
		var value = this.inputValue;
		if (value == null) return null;
		if (this.multiple) return value.map(this.type.toInputValue, this.type);
		return this.type.toInputValue(value);
	}, function (value) {
		if (value == null) {
			value = null;
		} else if (this.multiple) {
			if (isMap(value)) value = toArray(value).map(getMapValue);
			else value = toArray(value);
			if (value.length) {
				value = compact.call(value.map(this.type.toInputValue, this.type));
			}
			if (!value.length) value = null;
		} else {
			value = this.type.toInputValue(value);
		}

		this.inputValue = value;
	}),
	onChange: d(function () {
		var value, changed, valid, emitChanged, emitValid;
		value = this.inputValue;
		changed = (this.multiple && (this._value != null) && (value != null))
			? isCopy.call(value, this._value) : (value !== this._value);
		valid = this.required ? (value != null) : true;

		if (this.changed !== changed) {
			this.changed = changed;
			emitChanged = true;
		}
		if (this.valid !== valid) {
			this.valid = valid;
			emitValid = true;
		}

		this.emit('change', value);
		if (emitChanged) this.emit('change:changed', this.changed);
		if (emitValid) this.emit('change:valid', this.valid);
	}),
	removeItem: d(function (dom) {
		remove.call(dom);
		if (!this.multiple) this.dom.classList.remove('filled');
		dispatchEvnt.call(this.control, 'change', eventOpts);
	})
}, memoizeMethods({
	_renderItem: d(function (file) {
		var data;
		file = this.type.getById(file);
		data = this.renderItem(file);
		if (data.control) this.controls.push(data.control);
		return data.dom;
	})
})));

module.exports = memoize(function (db) {
	defineProperties(setup(db).File, {
		fromInputValue: d(function (value) {
			if (value == null) return null;
			if (value === '') return null;
			if (isArray(value) && (value.length === 2)) {
				if (typeof value[0] === 'string') {
					if (isObject(value[1])) return value[1];
				} else if (typeof value[1] === 'string') {
					if (isObject(value[0])) return value[0];
				}
				return value;
			}
			if (isObject(value)) return value;
			value = value.trim();
			if (!value) return null;
			return this.getById(value);
		}),
		DOMInput: d(Input),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple || (options.type !== 'checkbox')) {
				return new this.DOMInput(document, this, options);
			}
			return db.Object.toDOMInput(document, options);
		})
	});
}, { normalizer: getNormalizer() });

exports.Input = Input;
<|endoftext|>


<commit_before>var React = require('react');

var WalmartComponents = require('./Home-Walmart-Components');
var WalmartRelatedResultsDisplay = WalmartComponents.WalmartRelatedResultsDisplay;
var WalmartIndividualResultDisplay = WalmartComponents.WalmartIndividualResultDisplay;
var WalmartReviewsDisplay = WalmartComponents.WalmartReviewsDisplay;

var AmazonComponents = require('./Home-Amazon-Components');
var AmazonRelatedResultsDisplay = AmazonComponents.AmazonRelatedResultsDisplay;
var AmazonIndividualResultDisplay = AmazonComponents.AmazonIndividualResultDisplay;

var BestbuyComponents = require('./Home-Bestbuy-Components');
var BestbuyRelatedResultsDisplay = BestbuyComponents.BestbuyRelatedResultsDisplay;
var BestbuyIndividualResultDisplay = BestbuyComponents.BestbuyIndividualResultDisplay;
var BestbuyReviewsDisplay = BestbuyComponents.BestbuyReviewsDisplay;

var D3Components = require('./D3-Chart.js');
var D3Chart = D3Components.D3Chart;

// Centralized display for all components
var DisplayBox = React.createClass({
  // Sets initial state properties to empty arrays to avoid undefined errors
  getInitialState: function() {
    return {
      // We set the initial state to the format {'API name': [Array of results]}
      // to help organize the results we get back from the server, since the
      // general-query request returns results from three different APIs
      amazon: {amazon: []},
      walmart: {walmart: []},
      bestbuy: {bestbuy: []},
      walmartReviews: {walmartReviews: []},
      bestbuyReviews: {bestbuyReviews: []}
    };
  },

  // Called when user submits a query
  handleQuerySubmit: function(query) {
    $.ajax({
      url: 'general-query',
      dataType: 'json',
      type: 'POST',
      data: query,
      success: function(data) {
        // Show Related Results after user submits query
        $('.related-results-display').removeClass('hidden');

        // Set the state to contain data for each separate API
        // data[0] --> {walmart: [Array of Walmart results]}
        // data[1] --> {amazon: [Array of Amazon results]}
        // data[2] --> {bestbuy: [Array of Best Buy results]}
        this.setState({
          walmart: data[0],
          amazon: data[1],
          bestbuy: data[2]
        });

        // Hide the spinner after all API requests have been completed
        $('.query-form-container img').addClass('hidden');

      }.bind(this),
      error: function(xhr, status, err) {
        console.error('general-query', status, err.toString());
      }.bind(this)
    });
  },

  // Final handler for Walmart review request
  // This call is the result of calls bubbling up from the individual Walmart results
  handleWalmartReviewRequest: function(itemId, name, image) {

    // Sets the product name and image for the product clicked on (Revews Display)
    // These are passed up from WalmartIndividualResultDisplay
    this.setState({
      walmartReviewedItemName: name,
      walmartReviewedItemImage: image
    });

    // Makes a specific API call to get reviews for the product clicked on
    $.ajax({
      url: 'get-walmart-reviews',
      dataType: 'json',
      type: 'POST',
      // itemId is used to make a request for Walmart reviews
      data: itemId,
      success: function(data) {
        // Display the reviews-display only after an item is clicked on
        $('.walmart-reviews-display').removeClass('hidden');

        // Get the reviews array from the response data
        var walmartReviewsFromData = JSON.parse(data[0].walmartReviews).reviews;

        // Set the walmartReviews state in the same format as the 'general-query' states
        this.setState({
          walmartReviews: {walmartReviews: walmartReviewsFromData}
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error('get-walmart-reviews', status, err.toString());
      }.bind(this)
    });
  },
  handleBestbuyReviewRequest: function(sku, name, image) {

    this.setState({
      bestbuyReviewedItemName: name,
      bestbuyReviewedItemImage: image
    });

    $.ajax({
      url: 'get-bestbuy-reviews',
      dataType: 'json',
      type: 'POST',
      data: sku,
      success: function(data) {
        $('.bestbuy-reviews-display').removeClass('hidden');

        var bestbuyReviewsFromData = JSON.parse(data[0].bestbuyReviews).reviews;

        this.setState({
          bestbuyReviews: {bestbuyReviews: bestbuyReviewsFromData}
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error('get-bestbuy-reviews', status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    return (

        <SearchForm onQuerySubmit={this.handleQuerySubmit} />

        <D3Chart
          walmartData={this.state.walmartReviews}
          walmartName={this.state.walmartReviewedItemName}
          bestbuyData={this.state.bestbuyReviews}
          bestbuyName={this.state.bestbuyReviewedItemName} />

          <WalmartReviewsDisplay
            data={this.state.walmartReviews}
            name={this.state.walmartReviewedItemName}
            image={this.state.walmartReviewedItemImage} />
          <BestbuyReviewsDisplay
            data={this.state.bestbuyReviews}
            name={this.state.bestbuyReviewedItemName}
            image={this.state.bestbuyReviewedItemImage} />

        <AmazonRelatedResultsDisplay data={this.state.amazon} />
        <WalmartRelatedResultsDisplay
          data={this.state.walmart}
          onWalmartReviewRequest={this.handleWalmartReviewRequest} />
        <BestbuyRelatedResultsDisplay
          data={this.state.bestbuy}
          onBestbuyReviewRequest={this.handleBestbuyReviewRequest} />

    );
  }
});

// Component for the query-submit form
var SearchForm = React.createClass({
  handleSubmit: function(e) {
    // Prevent page from reloading on submit
    e.preventDefault();

    $('.query-form-container img').removeClass('hidden');

    // Grab query content from "ref" in input box
    var query = React.findDOMNode(this.refs.query).value.trim();

    // Passes the query to the central DisplayBox component
    // DisplayBox will make AJAX call and display results
    this.props.onQuerySubmit({query: query});

    // Clear the input box after submit
    React.findDOMNode(this.refs.query).value = '';
  },
  render: function() {
    return (

ShopChimp, at your service.

        <form className="query-form" onSubmit={this.handleSubmit}>
          <input type="text" placeholder="Enter a product" className="form-control" ref="query" />

          <button className="btn btn-primary">Submit</button>
        </form>

    );
  }
});

// Home page container for the DisplayBox component
var Home = React.createClass({
	render: function() {
		return (

        <DisplayBox />

		);
	}
});

module.exports = Home;<commit_msg>(doc) finish commenting Home.js<commit_after>var React = require('react');

var WalmartComponents = require('./Home-Walmart-Components');
var WalmartRelatedResultsDisplay = WalmartComponents.WalmartRelatedResultsDisplay;
var WalmartIndividualResultDisplay = WalmartComponents.WalmartIndividualResultDisplay;
var WalmartReviewsDisplay = WalmartComponents.WalmartReviewsDisplay;

var AmazonComponents = require('./Home-Amazon-Components');
var AmazonRelatedResultsDisplay = AmazonComponents.AmazonRelatedResultsDisplay;
var AmazonIndividualResultDisplay = AmazonComponents.AmazonIndividualResultDisplay;

var BestbuyComponents = require('./Home-Bestbuy-Components');
var BestbuyRelatedResultsDisplay = BestbuyComponents.BestbuyRelatedResultsDisplay;
var BestbuyIndividualResultDisplay = BestbuyComponents.BestbuyIndividualResultDisplay;
var BestbuyReviewsDisplay = BestbuyComponents.BestbuyReviewsDisplay;

var D3Components = require('./D3-Chart.js');
var D3Chart = D3Components.D3Chart;

// Centralized display for all components on the Home page
var DisplayBox = React.createClass({
  // Sets initial state properties to empty arrays to avoid undefined errors
  getInitialState: function() {
    return {
      // We set the initial state to the format {'API name': [Array of results]}
      // to help organize the results we get back from the server, since the
      // general-query request returns results from three different APIs
      amazon: {amazon: []},
      walmart: {walmart: []},
      bestbuy: {bestbuy: []},
      walmartReviews: {walmartReviews: []},
      bestbuyReviews: {bestbuyReviews: []}
    };
  },

  // Called when user submits a query
  handleQuerySubmit: function(query) {
    $.ajax({
      url: 'general-query',
      dataType: 'json',
      type: 'POST',
      data: query,
      success: function(data) {
        // Show Related Results after user submits query
        $('.related-results-display').removeClass('hidden');

        // Set the state to contain data for each separate API
        // data[0] --> {walmart: [Array of Walmart results]}
        // data[1] --> {amazon: [Array of Amazon results]}
        // data[2] --> {bestbuy: [Array of Best Buy results]}
        this.setState({
          walmart: data[0],
          amazon: data[1],
          bestbuy: data[2]
        });

        // Hide the spinner after all API requests have been completed
        $('.query-form-container img').addClass('hidden');

      }.bind(this),
      error: function(xhr, status, err) {
        console.error('general-query', status, err.toString());
      }.bind(this)
    });
  },

  // Final handler for Walmart review request
  // This call is the result of calls bubbling up from the individual Walmart results
  handleWalmartReviewRequest: function(itemId, name, image) {

    // Sets the product name and image for the product clicked on (Revews Display)
    // These are passed up from WalmartIndividualResultDisplay
    this.setState({
      walmartReviewedItemName: name,
      walmartReviewedItemImage: image
    });

    // Makes a specific API call to get reviews for the product clicked on
    $.ajax({
      url: 'get-walmart-reviews',
      dataType: 'json',
      type: 'POST',
      // itemId is used to make a request for Walmart reviews
      data: itemId,
      success: function(data) {
        // Display the reviews-display only after an item is clicked on
        $('.walmart-reviews-display').removeClass('hidden');

        // Get the reviews array from the response data
        var walmartReviewsFromData = JSON.parse(data[0].walmartReviews).reviews;

        // Set the walmartReviews state in the same format as the 'general-query' states
        this.setState({
          walmartReviews: {walmartReviews: walmartReviewsFromData}
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error('get-walmart-reviews', status, err.toString());
      }.bind(this)
    });
  },

  // Final handler for Best Buy review request
  // This call is the result of calls bubbling up from the individual Best Buy results
  handleBestbuyReviewRequest: function(sku, name, image) {

    // Final handler for Walmart review request
    // This call is the result of calls bubbling up from the individual Walmart results
    this.setState({
      bestbuyReviewedItemName: name,
      bestbuyReviewedItemImage: image
    });

    // Makes a specific API call to get reviews for the product clicked on
    $.ajax({
      url: 'get-bestbuy-reviews',
      dataType: 'json',
      type: 'POST',
      // sku is used to make a request for Best Buy reviews
      data: sku,
      success: function(data) {
        // Display the reviews-display only after an item is clicked on
        $('.bestbuy-reviews-display').removeClass('hidden');

        // Get the reviews array from the response data
        var bestbuyReviewsFromData = JSON.parse(data[0].bestbuyReviews).reviews;

        // Set the walmartReviews state in the same format as the 'general-query' states
        this.setState({
          bestbuyReviews: {bestbuyReviews: bestbuyReviewsFromData}
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error('get-bestbuy-reviews', status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    // Not sure how to comment (if we can) in the JSX React syntax below,
    // which compiles to JavaScript with "Reactify" (in server.js)
    // Attributes are "props" which can be accessed by the component
    // Many "props" are set as the "state", which is set based on data received from API calls
    return (

        <SearchForm onQuerySubmit={this.handleQuerySubmit} />

        <D3Chart
          walmartData={this.state.walmartReviews}
          walmartName={this.state.walmartReviewedItemName}
          bestbuyData={this.state.bestbuyReviews}
          bestbuyName={this.state.bestbuyReviewedItemName} />

          <WalmartReviewsDisplay
            data={this.state.walmartReviews}
            name={this.state.walmartReviewedItemName}
            image={this.state.walmartReviewedItemImage} />
          <BestbuyReviewsDisplay
            data={this.state.bestbuyReviews}
            name={this.state.bestbuyReviewedItemName}
            image={this.state.bestbuyReviewedItemImage} />

        <AmazonRelatedResultsDisplay data={this.state.amazon} />
        <WalmartRelatedResultsDisplay
          data={this.state.walmart}
          onWalmartReviewRequest={this.handleWalmartReviewRequest} />
        <BestbuyRelatedResultsDisplay
          data={this.state.bestbuy}
          onBestbuyReviewRequest={this.handleBestbuyReviewRequest} />

    );
  }
});

// Component for the query-submit form (general, not reviews)
var SearchForm = React.createClass({
  handleSubmit: function(e) {
    // Prevent page from reloading on submit
    e.preventDefault();

    // Show the spinner when a query is submitted
    $('.query-form-container img').removeClass('hidden');

    // Grab query content from "ref" in input box
    var query = React.findDOMNode(this.refs.query).value.trim();

    // Passes the query to the central DisplayBox component
    // DisplayBox will make AJAX call and display results
    this.props.onQuerySubmit({query: query});

    // Clear the input box after submit
    React.findDOMNode(this.refs.query).value = '';
  },
  render: function() {
    return (

ShopChimp, at your service.

        <form className="query-form" onSubmit={this.handleSubmit}>
          <input type="text" placeholder="Enter a product" className="form-control" ref="query" />

          <button className="btn btn-primary">Submit</button>
        </form>

    );
  }
});

// Home page container for the DisplayBox component
var Home = React.createClass({
	render: function() {
		return (

        <DisplayBox />

		);
	}
});

module.exports = Home;<|endoftext|>