<commit_before>/**
 * We.js main file, load we.js core and features
 */

// Module dependencies.

// Disable uneed bluebird warnings:
process.env.BLUEBIRD_W_FORGOTTEN_RETURN = '0';

const http = require('http'),
      _ = require('lodash'),
      path = require('path'),
      staticConfig = require('./staticConfig'),
      Database = require('./Database'),
      Hooks = require('./Hooks'),
      PluginManager = require('./PluginManager'),
      Router = require('./Router'),
      Sanitizer = require('./Sanitizer'),
      EventEmiter = require('events'),
      unhandledErrorCatcher = require('./unhandledErrorCatcher'),
      { readFileSync, writeFileSync, writeFile } = require('fs');

/**
 * We.js object
 *
 * @type {Object}
 */
function We (options) {
  if (!options) options = {};

  const we = this;

  this.packageJSON = require('../package.json');

  this.config = options;

  this.childProcesses = [];

  this.plugins = {};
  this.pluginPaths = [];
  this.pluginNames = [];
  // controllers
  this.controllers = {};

  this.projectPath = options.projectPath || process.cwd();

  this.projectPackageJSON = require(path.resolve(this.projectPath, 'package.json'));

  this.projectConfigFolder = options.projectConfigFolder || path.join(this.projectPath, 'config');

  // start configs with static configs
  this.config = staticConfig(this.projectPath, this);
  // enviroment config prod | dev | test
  we.env = options.env || require('./getEnv.js')();
  // winston logger
  we.log = require('./log')(we);
  // hooks and events
  we.hooks = new Hooks();

  we.events = new EventEmiter();
  // we.js events is controlled then we will use an max of 70 listenners in every event:
  we.events.setMaxListeners(70);
  // we.js sanitizer
  we.sanitizer = new Sanitizer(this);
  // The we.js router
  we.router = new Router(this);
  // we.js prototypes
  we.class = {
    Controller: require('./class/Controller.js'),
    Theme: require('./class/Theme.js')(we),
    Plugin: require('./class/Plugin.js')(we)
  };
  // database logic and models is avaible in we.db.models
  we.db = new Database(this);
  // - init database
  we.db.defaultConnection = we.db.connect(we.env);
  //set for more compatbility with sequelize docs
  we.db.sequelize = we.db.defaultConnection;
  // plugin manager and plugins vars
  we.pluginManager = new PluginManager(this);
  // Error catcher to get unhandled erros, use this options to disable on multiple we.js instances
  if (!we.config.disableGlobalErrorCatcher) {
    unhandledErrorCatcher(we);
  }

  switch (we.config.bootstrapMode) {
    case 'install':
    case 'installation':
    case 'update':
      we.hooks.on('bootstrap', [
        we.bootstrapFunctions.checkDBConnection,
        we.bootstrapFunctions.loadCoreFeatures,
        we.bootstrapFunctions.loadPluginFeatures,
        we.bootstrapFunctions.initI18n,
        we.bootstrapFunctions.loadTemplateCache,
        we.bootstrapFunctions.instantiateModels,
        we.bootstrapFunctions.syncModels,
        we.bootstrapFunctions.loadControllers,
        we.bootstrapFunctions.installAndRegisterPlugins
      ]);
      break;
    case 'complete':
    case 'full':
    case 'test':
      // full load, usefull for tests
      we.hooks.on('bootstrap', [
        we.bootstrapFunctions.checkDBConnection,
        we.bootstrapFunctions.loadCoreFeatures,
        we.bootstrapFunctions.loadPluginFeatures,
        we.bootstrapFunctions.initI18n,
        we.bootstrapFunctions.loadTemplateCache,
        we.bootstrapFunctions.instantiateModels,
        we.bootstrapFunctions.syncModels,
        we.bootstrapFunctions.loadControllers,
        we.bootstrapFunctions.installAndRegisterPlugins,
        we.bootstrapFunctions.setExpressApp,
        we.bootstrapFunctions.passport,
        we.bootstrapFunctions.createDefaultFolders,
        we.bootstrapFunctions.registerAllViewTemplates,
        we.bootstrapFunctions.mergeRoutes,
        we.bootstrapFunctions.bindResources,
        we.bootstrapFunctions.bindRoutes
      ]);
      break;
    default:
      // defaults to load for run
      we.hooks.on('bootstrap', [
        we.bootstrapFunctions.checkDBConnection,
        we.bootstrapFunctions.loadCoreFeatures,
        we.bootstrapFunctions.loadPluginFeatures,
        we.bootstrapFunctions.initI18n,
        we.bootstrapFunctions.loadTemplateCache,
        we.bootstrapFunctions.instantiateModels,
        we.bootstrapFunctions.syncModels,
        we.bootstrapFunctions.loadControllers,
        we.bootstrapFunctions.installAndRegisterPlugins,
        we.bootstrapFunctions.setExpressApp,
        we.bootstrapFunctions.passport,
        we.bootstrapFunctions.createDefaultFolders,
        we.bootstrapFunctions.registerAllViewTemplates,
        we.bootstrapFunctions.mergeRoutes,
        we.bootstrapFunctions.bindResources,
        we.bootstrapFunctions.bindRoutes
      ]);
  }
}

We.prototype = {
  /**
   * Set config in config/configuration.json file
   *
   * @param {String}   variable path to the variable
   * @param {String}   value
   * @param {Function} cb       callback
   */
  setConfig(variable, value, cb) {
    if (!cb) cb = function(){};

    let cJSON,
        cFGpath = path.join(this.projectPath, '/config/configuration.json');

    try {
      cJSON = JSON.parse(readFileSync(cFGpath));
    } catch(e) {
      if (e.code == 'ENOENT') {
        writeFileSync(cFGpath, '{}');
        cJSON = {};
      } else {
        return cb(e);
      }
    }

    if (value == 'true') value = true;
    if (value == 'false') value = false;

    _.set(cJSON, variable, value);

    writeFile(cFGpath, JSON.stringify(cJSON, null, 2), cb);
  },

  /**
   * Unset config in config/configuration.json file
   *
   * @param {String}   variable path to the variable
   * @param {String}   value
   * @param {Function} cb       callback
   */
  unSetConfig(variable, cb) {
    let cJSON,
        cFGpath = path.join(this.projectPath, '/config/configuration.json');

    try {
      cJSON = JSON.parse(readFileSync(cFGpath));
    } catch(e) {
      if (e.code == 'ENOENT') {
        writeFileSync(cFGpath, '{}');
        cJSON = {};
      } else {
        return cb(e);
      }
    }

    _.unset(cJSON, variable);

    writeFile(cFGpath, JSON.stringify(cJSON, null, 2), cb);
  },
  // set bootstrap functions
  bootstrapFunctions: require('./bootstrapFunctions'),
  // flag to check if this we.js instance did the bootstrap
  bootstrapStarted: false,
  // flag to check if needs restart
  needsRestart: false,
  // we.utils.async, we.utils._ ... see the ./utils file
  utils: require('./utils'),
  // load we.js responses
  responses: require('./responses'),
  // save we-core path to plugin.js for update e install process
  weCorePluginfile: path.resolve(__dirname, '../') + '/plugin.js',
  //Overide default toString and inspect to custom infos in we.js object
  inspect() {
    return '\nWe.js ;)\n';
  },
  toString() {
    return this.inspect();
  },
  // client side config generator
  getAppBootstrapConfig: require('./staticConfig/getAppBootstrapConfig.js'),

  /**
   * Bootstrap and initialize the app
   *
   * @param  {Object}   configOnRun optional
   * @param  {Function} cb          callback to run after load we.js
   */
  bootstrap(configOnRun, cb) {
    const we = this;
    // only bootstrap we.js one time
    if (we.bootstrapStarted) throw new Error('We.js already did bootstrap');

    we.bootstrapStarted = true;
    // configsOnRun object is optional
    if (!cb) {
      cb = configOnRun;
      configOnRun = null;
    }
    // configs on run extends default and file configs
    if (configOnRun) _.merge(we.config, configOnRun);

    // run the bootstrap hook
    we.hooks.trigger('bootstrap', we, function bootstrapDone (err) {
      if (err) {
        console.log('Error on bootstrap: ');
        throw err;
      }

      we.events.emit('we:bootstrap:done', we);

      we.log.debug('We.js bootstrap done');
      // set pm2 gracefullReload:
      we.setPM2grShutdownFN(we);

      return cb(null, we);
    });
  },

  /**
   * Start we.js server (express)
   * use after we.bootstrap
   *
   * @param  {Function} cb    callback how returns with cb(err);
   */
  startServer(cb) {
    if (!cb) cb = function(){}; // cb is optional

    let we = this;

    we.hooks.trigger('we:server:before:start', we, function afterRunBeforeServerStart (err) {
      if (err) return cb(err);
      /**
       * Get port from environment and store in Express.
       */
      let port = normalizePort(we.config.port);
      we.express.set('port', port);

      /**
       * Create HTTP server with suport to url alias rewrite
       */
      const server = http.createServer(function onCreateServer (req, res){
        Object.defineProperty(req, 'we', {
          get: function getWe() { return we; }
        });

        // Clean memory on end events:
        res.on('finish', function() {
          we.freeResponseMemory(req, res);
        });
        res.on('close', function() {
          we.freeResponseMemory(req, res);
        });
        res.on('end', function() {
          we.freeResponseMemory(req, res);
        });

        // suport for we.js widget API
        // install we-plugin-widget to enable this feature
        if (req.headers && req.headers['we-widget-action'] && req.method == 'POST') {
          req.isWidgetAction = true;
          req.originalMethod = req.method;
          req.method = 'GET'; // widgets only are associated to get routes
        }

        // parse extension if not is public folder
        if (!we.router.isPublicFolder(req.url)) {
          req.extension = we.router.splitExtensionFromURL(req.url);

          if (req.extension) {
            let format = we.utils.mime.getType(req.extension);

            if (req.we.config.responseTypes.includes(format)) {
              // update the header response format with extension format
              req.headers.accept = format;
              // update the old url
              req.url = req.url.replace('.'+req.extension, '');
            }
          }
        }
        // install we-plugin-url-alias to enable alias feature
        if (we.plugins['we-plugin-url-alias'] && we.config.enableUrlAlias) {
          we.router.alias.httpHandler.bind(this)(req, res);
        } else {
          we.express.bind(this)(req, res);
        }
      });

      we.events.emit('we:server:after:create', { we: we, server: server });

      /**
       * Listen on provided port, on all network interfaces.
       */

      // catch 404 and forward to error handler
      we.express.use(function routeNotFound (req, res) {
        we.log.debug('Route not found:', {
          method: req.method,
          path: req.path
        });
        // var err = new Error('Not Found');
        // err.status = 404;
        return res.notFound();
      });

      server.listen(port);
      server.on('error', onError);
      server.on('listening', onListening);
      /**
       * Normalize a port into a number, string, or false.
       */
      function normalizePort (val) {
        let port = parseInt(val, 10);

        if (isNaN(port)) {
          // named pipe
          return val;
        }

        if (port >= 0) {
          // port number
          return port;
        }

        return false;
      }

      /**
       * Event listener for HTTP server "error" event.
       */
      function onError (error) {
        if (error.syscall !== 'listen') {
          throw error;
        }

        let bind = typeof port === 'string'
          ? 'Pipe ' + port
          : 'Port ' + port;

        // handle specific listen errors with friendly messages
        switch (error.code) {
          case 'EACCES':
            console.error(bind + ' requires elevated privileges');
            process.exit(1);
            break;
          case 'EADDRINUSE':
            console.error(bind + ' is already in use');
            process.exit(1);
            break;
          default:
            throw error;
        }
      }
      /**
       * Event listener for HTTP server "listening" event.
       */
      function onListening () {
        let addr = server.address(),
            bind = typeof addr === 'string'
          ? 'pipe ' + addr
          : 'port ' + addr.port;
        we.log.info('Run in '+we.env+' enviroment and listening on ' + bind);

        if (process.send) {
          process.send('ready');
        }
      }
      // save the current http server
      we.http = server;

      // express error handlers
      // will print stacktrace
      we.express.use(function onExpressError (err, req, res, next) {
        // invalid url error handling
        if (err instanceof URIError) {
          we.log.warn('onExpressError:URIError on:', {
            path: req.path,
            error: err
          });

          res.addMessage('warning', {
            text: 'router.invalid.url'
          });

          // go to home page on invalid url request
          return res.goTo('/');
        }

        we.log.error('onExpressError:Error on:', {
          path: req.path,
          error: err
        });

        res.serverError(err);
      });

      we.hooks.trigger('we:server:after:start',we, function afterHookAfterStart (err) {
        cb(err, we);
      });
    });
  },

  /**
   * Bootstrap and Start we.js server
   *
   * @param  {Object}   cfgs configs (optional)
   * @param  {Function} cb   callback
   */
  go(cfgs, cb) {
    if (!cb) {
      cb = cfgs;
      cfgs = {};
    }

    this.bootstrap(cfgs, function afterBootstrapOnWeGo (err, we) {
      if (err) throw err;
      we.startServer(cb);
    });
  },

  /**
   * Turn off process function
   *
   * @param  {Function} cb callback
   */
  exit(cb) {
    this.isExiting = true;
    // close db connection
    if (this.db.defaultConnection) {
      this.db.defaultConnection.close();
    }
    // close all loggers and wait for end writes
    this.log.closeAllLoggersAndDisconnect(this, cb);
  },

  /**
   * Helper function to delete objects from response for help GC
   */
  freeResponseMemory(req, res) {
    if (res.locals) {
      res.locals.regions = {};
      res.locals.currentUser = {};
    }

    for(let name in res.locals) {
      delete res.locals[name];
    }

    delete req.user;
  },

  /**
   * Run all plugin and project cron tasks
   *
   * @param  {Function} cb callback
   */
  runCron(cb) {
    this.cron = require('./cron');
    this.cron.loadAndRunAllTasks(this, cb);
  },

  /**
   * set pm2 gracefullReload
   */
  setPM2grShutdownFN(we) {
    // old/windows version:
    process.on('message', (msg)=> {
      if (msg === 'shutdown') {
        // disconect, and exit
        we.exit( (err)=> { process.exit(err ? 1 : 0); });
      }
    });
    // new version:
    process.on('SIGINT', ()=> {
      we.exit( (err)=> { process.exit(err ? 1 : 0); });
    });
  }
};

// Add default toJSON method in errors to get better erros:
if (!('toJSON' in Error.prototype)) {
  Object.defineProperty(Error.prototype, 'toJSON', {
    value: function () {
      const alt = {};

      Object.getOwnPropertyNames(this).forEach(function (key) {
          alt[key] = this[key];
      }, this);

      return alt;
    },
    configurable: true,
    writable: true
  });
}

module.exports = We;<commit_msg>Improve resource usage<commit_after>/**
 * We.js main file, load we.js core and features
 */

// Module dependencies.

// Disable uneed bluebird warnings:
process.env.BLUEBIRD_W_FORGOTTEN_RETURN = '0';

const http = require('http'),
      _ = require('lodash'),
      path = require('path'),
      staticConfig = require('./staticConfig'),
      Database = require('./Database'),
      Hooks = require('./Hooks'),
      PluginManager = require('./PluginManager'),
      Router = require('./Router'),
      Sanitizer = require('./Sanitizer'),
      EventEmiter = require('events'),
      unhandledErrorCatcher = require('./unhandledErrorCatcher'),
      { readFileSync, writeFileSync, writeFile } = require('fs');

/**
 * We.js object
 *
 * @type {Object}
 */
function We (options) {
  if (!options) options = {};

  const we = this;

  this.packageJSON = require('../package.json');

  this.config = options;

  this.childProcesses = [];

  this.plugins = {};
  this.pluginPaths = [];
  this.pluginNames = [];
  // controllers
  this.controllers = {};

  this.projectPath = options.projectPath || process.cwd();

  this.projectPackageJSON = require(path.resolve(this.projectPath, 'package.json'));

  this.projectConfigFolder = options.projectConfigFolder || path.join(this.projectPath, 'config');

  // start configs with static configs
  this.config = staticConfig(this.projectPath, this);
  // enviroment config prod | dev | test
  we.env = options.env || require('./getEnv.js')();
  // winston logger
  we.log = require('./log')(we);
  // hooks and events
  we.hooks = new Hooks();

  we.events = new EventEmiter();
  // we.js events is controlled then we will use an max of 70 listenners in every event:
  we.events.setMaxListeners(70);
  // we.js sanitizer
  we.sanitizer = new Sanitizer(this);
  // The we.js router
  we.router = new Router(this);
  // we.js prototypes
  we.class = {
    Controller: require('./class/Controller.js'),
    Theme: require('./class/Theme.js')(we),
    Plugin: require('./class/Plugin.js')(we)
  };
  // database logic and models is avaible in we.db.models
  we.db = new Database(this);
  // - init database
  we.db.defaultConnection = we.db.connect(we.env);
  //set for more compatbility with sequelize docs
  we.db.sequelize = we.db.defaultConnection;
  // plugin manager and plugins vars
  we.pluginManager = new PluginManager(this);
  // Error catcher to get unhandled erros, use this options to disable on multiple we.js instances
  if (!we.config.disableGlobalErrorCatcher) {
    unhandledErrorCatcher(we);
  }

  switch (we.config.bootstrapMode) {
    case 'install':
    case 'installation':
    case 'update':
      we.hooks.on('bootstrap', [
        we.bootstrapFunctions.checkDBConnection,
        we.bootstrapFunctions.loadCoreFeatures,
        we.bootstrapFunctions.loadPluginFeatures,
        we.bootstrapFunctions.initI18n,
        we.bootstrapFunctions.loadTemplateCache,
        we.bootstrapFunctions.instantiateModels,
        we.bootstrapFunctions.syncModels,
        we.bootstrapFunctions.loadControllers,
        we.bootstrapFunctions.installAndRegisterPlugins
      ]);
      break;
    case 'complete':
    case 'full':
    case 'test':
      // full load, usefull for tests
      we.hooks.on('bootstrap', [
        we.bootstrapFunctions.checkDBConnection,
        we.bootstrapFunctions.loadCoreFeatures,
        we.bootstrapFunctions.loadPluginFeatures,
        we.bootstrapFunctions.initI18n,
        we.bootstrapFunctions.loadTemplateCache,
        we.bootstrapFunctions.instantiateModels,
        we.bootstrapFunctions.syncModels,
        we.bootstrapFunctions.loadControllers,
        we.bootstrapFunctions.installAndRegisterPlugins,
        we.bootstrapFunctions.setExpressApp,
        we.bootstrapFunctions.passport,
        we.bootstrapFunctions.createDefaultFolders,
        we.bootstrapFunctions.registerAllViewTemplates,
        we.bootstrapFunctions.mergeRoutes,
        we.bootstrapFunctions.bindResources,
        we.bootstrapFunctions.bindRoutes
      ]);
      break;
    default:
      // defaults to load for run
      we.hooks.on('bootstrap', [
        we.bootstrapFunctions.checkDBConnection,
        we.bootstrapFunctions.loadCoreFeatures,
        we.bootstrapFunctions.loadPluginFeatures,
        we.bootstrapFunctions.initI18n,
        we.bootstrapFunctions.loadTemplateCache,
        we.bootstrapFunctions.instantiateModels,
        we.bootstrapFunctions.syncModels,
        we.bootstrapFunctions.loadControllers,
        we.bootstrapFunctions.installAndRegisterPlugins,
        we.bootstrapFunctions.setExpressApp,
        we.bootstrapFunctions.passport,
        we.bootstrapFunctions.createDefaultFolders,
        we.bootstrapFunctions.registerAllViewTemplates,
        we.bootstrapFunctions.mergeRoutes,
        we.bootstrapFunctions.bindResources,
        we.bootstrapFunctions.bindRoutes
      ]);
  }
}

We.prototype = {
  /**
   * Set config in config/configuration.json file
   *
   * @param {String}   variable path to the variable
   * @param {String}   value
   * @param {Function} cb       callback
   */
  setConfig(variable, value, cb) {
    if (!cb) cb = function(){};

    let cJSON,
        cFGpath = path.join(this.projectPath, '/config/configuration.json');

    try {
      cJSON = JSON.parse(readFileSync(cFGpath));
    } catch(e) {
      if (e.code == 'ENOENT') {
        writeFileSync(cFGpath, '{}');
        cJSON = {};
      } else {
        return cb(e);
      }
    }

    if (value == 'true') value = true;
    if (value == 'false') value = false;

    _.set(cJSON, variable, value);

    writeFile(cFGpath, JSON.stringify(cJSON, null, 2), cb);
  },

  /**
   * Unset config in config/configuration.json file
   *
   * @param {String}   variable path to the variable
   * @param {String}   value
   * @param {Function} cb       callback
   */
  unSetConfig(variable, cb) {
    let cJSON,
        cFGpath = path.join(this.projectPath, '/config/configuration.json');

    try {
      cJSON = JSON.parse(readFileSync(cFGpath));
    } catch(e) {
      if (e.code == 'ENOENT') {
        writeFileSync(cFGpath, '{}');
        cJSON = {};
      } else {
        return cb(e);
      }
    }

    _.unset(cJSON, variable);

    writeFile(cFGpath, JSON.stringify(cJSON, null, 2), cb);
  },
  // set bootstrap functions
  bootstrapFunctions: require('./bootstrapFunctions'),
  // flag to check if this we.js instance did the bootstrap
  bootstrapStarted: false,
  // flag to check if needs restart
  needsRestart: false,
  // we.utils.async, we.utils._ ... see the ./utils file
  utils: require('./utils'),
  // load we.js responses
  responses: require('./responses'),
  // save we-core path to plugin.js for update e install process
  weCorePluginfile: path.resolve(__dirname, '../') + '/plugin.js',
  //Overide default toString and inspect to custom infos in we.js object
  inspect() {
    return '\nWe.js ;)\n';
  },
  toString() {
    return this.inspect();
  },
  // client side config generator
  getAppBootstrapConfig: require('./staticConfig/getAppBootstrapConfig.js'),

  /**
   * Bootstrap and initialize the app
   *
   * @param  {Object}   configOnRun optional
   * @param  {Function} cb          callback to run after load we.js
   */
  bootstrap(configOnRun, cb) {
    const we = this;
    // only bootstrap we.js one time
    if (we.bootstrapStarted) throw new Error('We.js already did bootstrap');

    we.bootstrapStarted = true;
    // configsOnRun object is optional
    if (!cb) {
      cb = configOnRun;
      configOnRun = null;
    }
    // configs on run extends default and file configs
    if (configOnRun) _.merge(we.config, configOnRun);

    // run the bootstrap hook
    we.hooks.trigger('bootstrap', we, function bootstrapDone (err) {
      if (err) {
        console.log('Error on bootstrap: ');
        throw err;
      }

      we.events.emit('we:bootstrap:done', we);

      we.log.debug('We.js bootstrap done');
      // set pm2 gracefullReload:
      we.setPM2grShutdownFN(we);

      return cb(null, we);
    });
  },

  /**
   * Start we.js server (express)
   * use after we.bootstrap
   *
   * @param  {Function} cb    callback how returns with cb(err);
   */
  startServer(cb) {
    if (!cb) cb = function(){}; // cb is optional

    let we = this;

    we.hooks.trigger('we:server:before:start', we, function afterRunBeforeServerStart (err) {
      if (err) return cb(err);
      /**
       * Get port from environment and store in Express.
       */
      let port = normalizePort(we.config.port);
      we.express.set('port', port);

      /**
       * Create HTTP server with suport to url alias rewrite
       */
      const server = http.createServer(function onCreateServer (req, res){
        Object.defineProperty(req, 'we', {
          get: function getWe() { return we; }
        });

        // Clean memory on end events:
        res.on('finish', function() {
          we.freeResponseMemory(req, res);
        });
        res.on('close', function() {
          we.freeResponseMemory(req, res);
        });
        res.on('end', function() {
          we.freeResponseMemory(req, res);
        });

        // suport for we.js widget API
        // install we-plugin-widget to enable this feature
        if (req.headers && req.headers['we-widget-action'] && req.method == 'POST') {
          req.isWidgetAction = true;
          req.originalMethod = req.method;
          req.method = 'GET'; // widgets only are associated to get routes
        }

        // parse extension if not is public folder
        if (!we.router.isPublicFolder(req.url)) {
          req.extension = we.router.splitExtensionFromURL(req.url);

          if (req.extension) {
            let format = we.utils.mime.getType(req.extension);

            if (req.we.config.responseTypes.includes(format)) {
              // update the header response format with extension format
              req.headers.accept = format;
              // update the old url
              req.url = req.url.replace('.'+req.extension, '');
            }
          }
        }
        // install we-plugin-url-alias to enable alias feature
        if (we.plugins['we-plugin-url-alias'] && we.config.enableUrlAlias) {
          we.router.alias.httpHandler.bind(this)(req, res);
        } else {
          we.express.bind(this)(req, res);
        }
      });

      we.events.emit('we:server:after:create', { we: we, server: server });

      /**
       * Listen on provided port, on all network interfaces.
       */

      // catch 404 and forward to error handler
      we.express.use(function routeNotFound (req, res) {
        we.log.debug('Route not found:', {
          method: req.method,
          path: req.path
        });
        // var err = new Error('Not Found');
        // err.status = 404;
        return res.notFound();
      });

      server.listen(port);
      server.on('error', onError);
      server.on('listening', onListening);
      /**
       * Normalize a port into a number, string, or false.
       */
      function normalizePort (val) {
        let port = parseInt(val, 10);

        if (isNaN(port)) {
          // named pipe
          return val;
        }

        if (port >= 0) {
          // port number
          return port;
        }

        return false;
      }

      /**
       * Event listener for HTTP server "error" event.
       */
      function onError (error) {
        if (error.syscall !== 'listen') {
          throw error;
        }

        let bind = typeof port === 'string'
          ? 'Pipe ' + port
          : 'Port ' + port;

        // handle specific listen errors with friendly messages
        switch (error.code) {
          case 'EACCES':
            console.error(bind + ' requires elevated privileges');
            process.exit(1);
            break;
          case 'EADDRINUSE':
            console.error(bind + ' is already in use');
            process.exit(1);
            break;
          default:
            throw error;
        }
      }
      /**
       * Event listener for HTTP server "listening" event.
       */
      function onListening () {
        let addr = server.address(),
            bind = typeof addr === 'string'
          ? 'pipe ' + addr
          : 'port ' + addr.port;
        we.log.info('Run in '+we.env+' enviroment and listening on ' + bind);

        if (process.send) {
          process.send('ready');
        }
      }
      // save the current http server
      we.http = server;

      // express error handlers
      // will print stacktrace
      we.express.use(function onExpressError (err, req, res, next) {
        // invalid url error handling
        if (err instanceof URIError) {
          we.log.warn('onExpressError:URIError on:', {
            path: req.path,
            error: err
          });

          res.addMessage('warning', {
            text: 'router.invalid.url'
          });

          // go to home page on invalid url request
          return res.goTo('/');
        }

        we.log.error('onExpressError:Error on:', {
          path: req.path,
          error: err
        });

        res.serverError(err);
      });

      we.hooks.trigger('we:server:after:start',we, function afterHookAfterStart (err) {
        cb(err, we);
      });
    });
  },

  /**
   * Bootstrap and Start we.js server
   *
   * @param  {Object}   cfgs configs (optional)
   * @param  {Function} cb   callback
   */
  go(cfgs, cb) {
    if (!cb) {
      cb = cfgs;
      cfgs = {};
    }

    this.bootstrap(cfgs, function afterBootstrapOnWeGo (err, we) {
      if (err) throw err;
      we.startServer(cb);
    });
  },

  /**
   * Turn off process function
   *
   * @param  {Function} cb callback
   */
  exit(cb) {
    this.isExiting = true;
    // close db connection
    if (this.db.defaultConnection) {
      this.db.defaultConnection.close();
    }
    // close all loggers and wait for end writes
    this.log.closeAllLoggersAndDisconnect(this, cb);
  },

  /**
   * Helper function to delete objects from response for help GC
   */
  freeResponseMemory(req, res) {
    if (res.locals) {
      res.locals.regions = {};
      res.locals.currentUser = {};
    }

    delete req.user;
  },

  /**
   * Run all plugin and project cron tasks
   *
   * @param  {Function} cb callback
   */
  runCron(cb) {
    this.cron = require('./cron');
    this.cron.loadAndRunAllTasks(this, cb);
  },

  /**
   * set pm2 gracefullReload
   */
  setPM2grShutdownFN(we) {
    // old/windows version:
    process.on('message', (msg)=> {
      if (msg === 'shutdown') {
        // disconect, and exit
        we.exit( (err)=> { process.exit(err ? 1 : 0); });
      }
    });
    // new version:
    process.on('SIGINT', ()=> {
      we.exit( (err)=> { process.exit(err ? 1 : 0); });
    });
  }
};

// Add default toJSON method in errors to get better erros:
if (!('toJSON' in Error.prototype)) {
  Object.defineProperty(Error.prototype, 'toJSON', {
    value: function () {
      const alt = {};

      Object.getOwnPropertyNames(this).forEach(function (key) {
          alt[key] = this[key];
      }, this);

      return alt;
    },
    configurable: true,
    writable: true
  });
}

module.exports = We;<|endoftext|>


<commit_before>;(function(window, document, undefined){
  'use strict'

  var options = {
    searchInput: null,
    resultsContainer: null,
    json: [],
    searchResultTemplate: '
{title}
',
    templateMiddleware: function(){},
    noResultsText: 'No results found',
    limit: 10,
    fuzzy: false,
    exclude: []
  }

  var requiredOptions = ['searchInput','resultsContainer','json']

  var templater = require('./Templater')
  var repository = require('./Repository')
  var jsonLoader = require('./JSONLoader')
  var optionsValidator = require('./OptionsValidator')({
    required: requiredOptions
  })
  var utils = require('./utils')

  /*
    Public API
  */
  window.SimpleJekyllSearch = function SimpleJekyllSearch(_options){
    var errors = optionsValidator.validate(_options)
    if( errors.length > 0 ){
      throwError('You must specify the following required options: ' + requiredOptions)
    }

    options = utils.merge(options, _options)

    templater.setOptions({
      template: options.searchResultTemplate,
      middleware: options.templateMiddleware,
    })

    repository.setOptions({
      fuzzy: options.fuzzy,
      limit: options.limit,
    })

    if( utils.isJSON(options.json) ){
      initWithJSON(options.json)
    }else{
      initWithURL(options.json)
    }
  }

  // for backwards compatibility
  window.SimpleJekyllSearch.init = window.SimpleJekyllSearch

  function initWithJSON(json){
    repository.put(json)
    registerInput()
  }

  function initWithURL(url){
    jsonLoader.load(url, function(err,json){
      if( err ){
        throwError('failed to get JSON (' + url + ')')
      }
      initWithJSON(json)
    })
  }

  function emptyResultsContainer(){
    options.resultsContainer.innerHTML = ''
  }

  function appendToResultsContainer(text){
    options.resultsContainer.innerHTML += text
  }

  function registerInput(){
    options.searchInput.addEventListener('keyup', function(e){

      // whitelist the following keycodes
      var whitelist = [13,16,20,37,38,39,40,91];

      var keyCodes = whitelist.map(function(code){
        return 'e.which != ' + code;
      }).join(' && ');

      // if the key pressed isn't one of the following whitelisted codes precede
      if( eval(keyCodes) ){
        emptyResultsContainer();
        if( e.target.value.length > 0 ){
          render( repository.search(e.target.value) );
        }
      }

    })
  }

  function render(results){
    if( results.length === 0 ){
      return appendToResultsContainer(options.noResultsText)
    }
    for (var i = 0; i < results.length; i++) {
      appendToResultsContainer( templater.compile(results[i]) )
    }
  }

  function throwError(message){ throw new Error('SimpleJekyllSearch --- '+ message) }
})(window, document);<commit_msg>simplified conditional upon request<commit_after>;(function(window, document, undefined){
  'use strict'

  var options = {
    searchInput: null,
    resultsContainer: null,
    json: [],
    searchResultTemplate: '
{title}
',
    templateMiddleware: function(){},
    noResultsText: 'No results found',
    limit: 10,
    fuzzy: false,
    exclude: []
  }

  var requiredOptions = ['searchInput','resultsContainer','json']

  var templater = require('./Templater')
  var repository = require('./Repository')
  var jsonLoader = require('./JSONLoader')
  var optionsValidator = require('./OptionsValidator')({
    required: requiredOptions
  })
  var utils = require('./utils')

  /*
    Public API
  */
  window.SimpleJekyllSearch = function SimpleJekyllSearch(_options){
    var errors = optionsValidator.validate(_options)
    if( errors.length > 0 ){
      throwError('You must specify the following required options: ' + requiredOptions)
    }

    options = utils.merge(options, _options)

    templater.setOptions({
      template: options.searchResultTemplate,
      middleware: options.templateMiddleware,
    })

    repository.setOptions({
      fuzzy: options.fuzzy,
      limit: options.limit,
    })

    if( utils.isJSON(options.json) ){
      initWithJSON(options.json)
    }else{
      initWithURL(options.json)
    }
  }

  // for backwards compatibility
  window.SimpleJekyllSearch.init = window.SimpleJekyllSearch

  function initWithJSON(json){
    repository.put(json)
    registerInput()
  }

  function initWithURL(url){
    jsonLoader.load(url, function(err,json){
      if( err ){
        throwError('failed to get JSON (' + url + ')')
      }
      initWithJSON(json)
    })
  }

  function emptyResultsContainer(){
    options.resultsContainer.innerHTML = ''
  }

  function appendToResultsContainer(text){
    options.resultsContainer.innerHTML += text
  }

  function registerInput(){
    options.searchInput.addEventListener('keyup', function(e){

      // whitelist the following keycodes
      var whitelist = [13,16,20,37,38,39,40,91];

      // if the key pressed isn't whitelisted continue
      if( whitelist.indexOf(e.which) === -1 ) {
        emptyResultsContainer();
        if( e.target.value.length > 0 ){
          render( repository.search(e.target.value) );
        }
      }

    })
  }

  function render(results){
    if( results.length === 0 ){
      return appendToResultsContainer(options.noResultsText)
    }
    for (var i = 0; i < results.length; i++) {
      appendToResultsContainer( templater.compile(results[i]) )
    }
  }

  function throwError(message){ throw new Error('SimpleJekyllSearch --- '+ message) }
})(window, document);<|endoftext|>


<commit_before>import middleware from './internal/middleware'
export default middleware

import _SagaCancellationException from './internal/SagaCancellationException'
export const SagaCancellationException = _SagaCancellationException
export const isCancelError = error => error instanceof SagaCancellationException

export { runSaga, storeIO } from './internal/runSaga'
export { END, channel } from './internal/channel'
export { takeEvery, takeLatest } from './internal/sagaHelpers'

import * as effects from './effects'
import * as utils from './utils'

export {
  effects,
  utils
}
<commit_msg>export eventChannel and END<commit_after>import middleware from './internal/middleware'
export default middleware

import _SagaCancellationException from './internal/SagaCancellationException'
export const SagaCancellationException = _SagaCancellationException
export const isCancelError = error => error instanceof SagaCancellationException

export { runSaga } from './internal/runSaga'
export { CANCEL } from './internal/proc'
export { END, channel, eventChannel } from './internal/channel'
export { takeEvery, takeLatest } from './internal/sagaHelpers'

import * as effects from './effects'
import * as utils from './utils'

export {
  effects,
  utils
}
<|endoftext|>


<commit_before>import tweenState from 'react-tween-state';
import NativeButton from './NativeButton';
import styles from './styles';

import React, {
  Component,
} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';

import {
  PanResponder,
  TouchableHighlight,
  StyleSheet,
  Text,
  View,
  ViewPropTypes,
} from 'react-native';

const SwipeoutBtn = createReactClass({

  propTypes: {
    backgroundColor: PropTypes.string,
    color: PropTypes.string,
    component: PropTypes.node,
    onPress: PropTypes.func,
    text: PropTypes.node,
    type: PropTypes.string,
    underlayColor: PropTypes.string,
  },

  getDefaultProps: function () {
    return {
      backgroundColor: null,
      color: null,
      component: null,
      underlayColor: null,
      height: 0,
      onPress: null,
      disabled: false,
      text: 'Click me',
      type: '',
      width: 0,
    };
  },

  render: function () {
    var btn = this.props;

    var styleSwipeoutBtn = [styles.swipeoutBtn];

    //  apply "type" styles (delete || primary || secondary)
    if (btn.type === 'delete') styleSwipeoutBtn.push(styles.colorDelete);
    else if (btn.type === 'primary') styleSwipeoutBtn.push(styles.colorPrimary);
    else if (btn.type === 'secondary') styleSwipeoutBtn.push(styles.colorSecondary);

    //  apply background color
    if (btn.backgroundColor) styleSwipeoutBtn.push([{ backgroundColor: btn.backgroundColor }]);

    styleSwipeoutBtn.push([{
      height: btn.height,
      width: btn.width,
    }]);

    var styleSwipeoutBtnComponent = [];

    //  set button dimensions
    styleSwipeoutBtnComponent.push([{
      height: btn.height,
      width: btn.width,
    }]);

    var styleSwipeoutBtnText = [styles.swipeoutBtnText];

    //  apply text color
    if (btn.color) styleSwipeoutBtnText.push({color: btn.color });

    return (
      <NativeButton
        onPress={this.props.onPress}
        underlayColor={this.props.underlayColor}
        disabled={this.props.disabled}
        style={[styles.swipeoutBtnTouchable, styleSwipeoutBtn]}
        textStyle={styleSwipeoutBtnText}>
        {
          (btn.component ?
            <View style={styleSwipeoutBtnComponent}>{btn.component}</View>
            :
            btn.text
          )
        }
      </NativeButton>
    );
  }
});

const Swipeout = createReactClass({
  mixins: [tweenState.Mixin],

  propTypes: {
    autoClose: PropTypes.bool,
    backgroundColor: PropTypes.string,
    close: PropTypes.bool,
    left: PropTypes.array,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
    right: PropTypes.array,
    scroll: PropTypes.func,
    style: (ViewPropTypes || View.propTypes).style,
    sensitivity: PropTypes.number,
    buttonWidth: PropTypes.number,
    disabled: PropTypes.bool,
  },

  getDefaultProps: function () {
    return {
      disabled: false,
      rowID: -1,
      sectionID: -1,
      sensitivity: 50,
    };
  },

  getInitialState: function () {
    return {
      autoClose: this.props.autoClose || false,
      btnWidth: 0,
      btnsLeftWidth: 0,
      btnsRightWidth: 0,
      contentHeight: 0,
      contentPos: 0,
      contentWidth: 0,
      openedRight: false,
      swiping: false,
      tweenDuration: 160,
      timeStart: null,
    };
  },

  componentWillMount: function () {
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: (event, gestureState) => true,
      onStartShouldSetPanResponderCapture: (event, gestureState) =>
        this.state.openedLeft || this.state.openedRight,
      onMoveShouldSetPanResponderCapture: (event, gestureState) =>
        Math.abs(gestureState.dx) > this.props.sensitivity &&
        Math.abs(gestureState.dy) <= this.props.sensitivity,
      onPanResponderGrant: this._handlePanResponderGrant,
      onPanResponderMove: this._handlePanResponderMove,
      onPanResponderRelease: this._handlePanResponderEnd,
      onPanResponderTerminate: this._handlePanResponderEnd,
      onShouldBlockNativeResponder: (event, gestureState) => false,
      onPanResponderTerminationRequest: () => false,
    });
  },

  componentWillReceiveProps: function (nextProps) {
    if (nextProps.close) this._close();
    if (nextProps.openRight) this._openRight();
    if (nextProps.openLeft) this._openLeft();
  },

  _handlePanResponderGrant: function (e: Object, gestureState: Object) {
    if (this.props.disabled) return;
    if (!this.state.openedLeft && !this.state.openedRight) {
      this._callOnOpen();
    } else {
      this._callOnClose();
    }
    this.refs.swipeoutContent.measure((ox, oy, width, height) => {
      let buttonWidth = this.props.buttonWidth || (width / 5);
      this.setState({
        btnWidth: buttonWidth,
        btnsLeftWidth: this.props.left ? buttonWidth * this.props.left.length : 0,
        btnsRightWidth: this.props.right ? buttonWidth * this.props.right.length : 0,
        swiping: true,
        timeStart: (new Date()).getTime(),
      });
    });
  },

  _handlePanResponderMove: function (e: Object, gestureState: Object) {
    if (this.props.disabled) return;
    var posX = gestureState.dx;
    var posY = gestureState.dy;
    var leftWidth = this.state.btnsLeftWidth;
    var rightWidth = this.state.btnsRightWidth;
    if (this.state.openedRight) var posX = gestureState.dx - rightWidth;
    else if (this.state.openedLeft) var posX = gestureState.dx + leftWidth;

    //  prevent scroll if moveX is true
    var moveX = Math.abs(posX) > Math.abs(posY);
    if (this.props.scroll) {
      if (moveX) this.props.scroll(false);
      else this.props.scroll(true);
    }
    if (this.state.swiping) {
      //  move content to reveal swipeout
      if (posX < 0 && this.props.right) {
        this.setState({ contentPos: Math.min(posX, 0) })
      } else if (posX > 0 && this.props.left) {
        this.setState({ contentPos: Math.max(posX, 0) })
      };
    }
  },

  _handlePanResponderEnd: function (e: Object, gestureState: Object) {
    if (this.props.disabled) return;
    var posX = gestureState.dx;
    var contentPos = this.state.contentPos;
    var contentWidth = this.state.contentWidth;
    var btnsLeftWidth = this.state.btnsLeftWidth;
    var btnsRightWidth = this.state.btnsRightWidth;

    //  minimum threshold to open swipeout
    var openX = contentWidth * 0.33;

    //  should open swipeout
    var openLeft = posX > openX || posX > btnsLeftWidth / 2;
    var openRight = posX < -openX || posX < -btnsRightWidth / 2;

    //  account for open swipeouts
    if (this.state.openedRight) var openRight = posX - openX < -openX;
    if (this.state.openedLeft) var openLeft = posX + openX > openX;

    //  reveal swipeout on quick swipe
    var timeDiff = (new Date()).getTime() - this.state.timeStart < 200;
    if (timeDiff) {
      var openRight = posX < -openX / 10 && !this.state.openedLeft;
      var openLeft = posX > openX / 10 && !this.state.openedRight;
    }

    if (this.state.swiping) {
      if (openRight && contentPos < 0 && posX < 0) {
        this._open(-btnsRightWidth, 'right');
      } else if (openLeft && contentPos > 0 && posX > 0) {
        this._open(btnsLeftWidth, 'left');
      } else {
        this._close();
      }
    }

    //  Allow scroll
    if (this.props.scroll) this.props.scroll(true);
  },

  _tweenContent: function (state, endValue) {
    this.tweenState(state, {
      easing: tweenState.easingTypes.easeInOutQuad,
      duration: endValue === 0 ? this.state.tweenDuration * 1.5 : this.state.tweenDuration,
      endValue: endValue,
    });
  },

  _rubberBandEasing: function (value, limit) {
    if (value < 0 && value < limit) return limit - Math.pow(limit - value, 0.85);
    else if (value > 0 && value > limit) return limit + Math.pow(value - limit, 0.85);
    return value;
  },

  //  close swipeout on button press
  _autoClose: function (btn) {
    if (this.state.autoClose) this._close();
    var onPress = btn.onPress;
    if (onPress) onPress();
  },

  _open: function (contentPos, direction) {
    const left = direction === 'left';
    const { sectionID, rowID, onOpen } = this.props;
    onOpen && onOpen(sectionID, rowID, direction);
    this._tweenContent('contentPos', contentPos);
    this.setState({
      contentPos,
      openedLeft: left,
      openedRight: !left,
      swiping: false,
    });
  },

  _close: function () {
    const { sectionID, rowID, onClose } = this.props;
    if (onClose && (this.state.openedLeft || this.state.openedRight)) {
      const direction = this.state.openedRight ? 'right' : 'left';
      onClose(sectionID, rowID, direction);
    }
    this._tweenContent('contentPos', 0);
    this._callOnClose();
    this.setState({
      openedRight: false,
      openedLeft: false,
      swiping: false,
    });
  },

  _callOnClose: function () {
    if (this.props.onClose) this.props.onClose(this.props.sectionID, this.props.rowID);
  },

  _callOnOpen: function () {
    if (this.props.onOpen) this.props.onOpen(this.props.sectionID, this.props.rowID);
  },

  _openRight: function () {
    this.refs.swipeoutContent.measure((ox, oy, width, height) => {
      let btnWidth = this.props.buttonWidth || (width / 5);

      this.setState({
        btnWidth,
        btnsRightWidth: this.props.right ? btnWidth * this.props.right.length : 0,
      }, () => {
        this._tweenContent('contentPos', -this.state.btnsRightWidth);
        this._callOnOpen();
        this.setState({
          contentPos: -this.state.btnsRightWidth,
          openedLeft: false,
          openedRight: true,
          swiping: false
        });
      });
    });
  },

  _openLeft: function () {
    this.refs.swipeoutContent.measure((ox, oy, width, height) => {
      let btnWidth = this.props.buttonWidth || (width / 5);

      this.setState({
        btnWidth,
        btnsLeftWidth: this.props.left ? btnWidth * this.props.left.length : 0,
      }, () => {
        this._tweenContent('contentPos', this.state.btnsLeftWidth);
        this._callOnOpen();
        this.setState({
          contentPos: this.state.btnsLeftWidth,
          openedLeft: true,
          openedRight: false,
          swiping: false
        });
      });
    });
  },

  render: function () {
    var contentWidth = this.state.contentWidth;
    var posX = this.getTweeningValue('contentPos');

    var styleSwipeout = [styles.swipeout, this.props.style];
    if (this.props.backgroundColor) {
      styleSwipeout.push([{ backgroundColor: this.props.backgroundColor }]);
    }

    var limit = -this.state.btnsRightWidth;
    if (posX > 0) var limit = this.state.btnsLeftWidth;

    var styleLeftPos = {
      left: {
        left: 0,
        overflow: 'hidden',
        width: Math.min(limit * (posX / limit), limit),
      },
    };
    var styleRightPos = {
      right: {
        left: Math.abs(contentWidth + Math.max(limit, posX)),
        right: 0,
      },
    };
    var styleContentPos = {
      content: {
        transform: [{ translateX: this._rubberBandEasing(posX, limit) }],
      },
    };

    var styleContent = [styles.swipeoutContent];
    styleContent.push(styleContentPos.content);

    var styleRight = [styles.swipeoutBtns];
    styleRight.push(styleRightPos.right);

    var styleLeft = [styles.swipeoutBtns];
    styleLeft.push(styleLeftPos.left);

    var isRightVisible = posX < 0;
    var isLeftVisible = posX > 0;

    return (
      <View style={styleSwipeout}>
        <View
          ref="swipeoutContent"
          style={styleContent}
          onLayout={this._onLayout}
          {...this._panResponder.panHandlers}
        >
          {this.props.children}
        </View>
        {this._renderButtons(this.props.right, isRightVisible, styleRight)}
        {this._renderButtons(this.props.left, isLeftVisible, styleLeft)}
      </View>
    );
  },

  _onLayout: function (event) {
    var { width, height } = event.nativeEvent.layout;
    this.setState({
      contentWidth: width,
      contentHeight: height,
    });
  },

  _renderButtons: function (buttons, isVisible, style) {
    if (buttons && isVisible) {
      return (<View style={style}>
        {buttons.map(this._renderButton)}
      </View>);
    } else {
      return (
        <View />
      );
    }
  },

  _renderButton: function (btn, i) {
    return (
      <SwipeoutBtn
        backgroundColor={btn.backgroundColor}
        color={btn.color}
        component={btn.component}
        disabled={btn.disabled}
        height={this.state.contentHeight}
        key={i}
        onPress={() => this._autoClose(btn)}
        text={btn.text}
        type={btn.type}
        underlayColor={btn.underlayColor}
        width={this.state.btnWidth}
      />
    );
  }
})

Swipeout.NativeButton = NativeButton;
Swipeout.SwipeoutButton = SwipeoutBtn;

export default Swipeout;
<commit_msg>Updated refs usage<commit_after>import tweenState from 'react-tween-state';
import NativeButton from './NativeButton';
import styles from './styles';

import React, {
  Component,
} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';

import {
  PanResponder,
  TouchableHighlight,
  StyleSheet,
  Text,
  View,
  ViewPropTypes,
} from 'react-native';

const SwipeoutBtn = createReactClass({

  propTypes: {
    backgroundColor: PropTypes.string,
    color: PropTypes.string,
    component: PropTypes.node,
    onPress: PropTypes.func,
    text: PropTypes.node,
    type: PropTypes.string,
    underlayColor: PropTypes.string,
  },

  getDefaultProps: function () {
    return {
      backgroundColor: null,
      color: null,
      component: null,
      underlayColor: null,
      height: 0,
      onPress: null,
      disabled: false,
      text: 'Click me',
      type: '',
      width: 0,
    };
  },

  render: function () {
    var btn = this.props;

    var styleSwipeoutBtn = [styles.swipeoutBtn];

    //  apply "type" styles (delete || primary || secondary)
    if (btn.type === 'delete') styleSwipeoutBtn.push(styles.colorDelete);
    else if (btn.type === 'primary') styleSwipeoutBtn.push(styles.colorPrimary);
    else if (btn.type === 'secondary') styleSwipeoutBtn.push(styles.colorSecondary);

    //  apply background color
    if (btn.backgroundColor) styleSwipeoutBtn.push([{ backgroundColor: btn.backgroundColor }]);

    styleSwipeoutBtn.push([{
      height: btn.height,
      width: btn.width,
    }]);

    var styleSwipeoutBtnComponent = [];

    //  set button dimensions
    styleSwipeoutBtnComponent.push([{
      height: btn.height,
      width: btn.width,
    }]);

    var styleSwipeoutBtnText = [styles.swipeoutBtnText];

    //  apply text color
    if (btn.color) styleSwipeoutBtnText.push({color: btn.color });

    return (
      <NativeButton
        onPress={this.props.onPress}
        underlayColor={this.props.underlayColor}
        disabled={this.props.disabled}
        style={[styles.swipeoutBtnTouchable, styleSwipeoutBtn]}
        textStyle={styleSwipeoutBtnText}>
        {
          (btn.component ?
            <View style={styleSwipeoutBtnComponent}>{btn.component}</View>
            :
            btn.text
          )
        }
      </NativeButton>
    );
  }
});

const Swipeout = createReactClass({
  mixins: [tweenState.Mixin],

  propTypes: {
    autoClose: PropTypes.bool,
    backgroundColor: PropTypes.string,
    close: PropTypes.bool,
    left: PropTypes.array,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
    right: PropTypes.array,
    scroll: PropTypes.func,
    style: (ViewPropTypes || View.propTypes).style,
    sensitivity: PropTypes.number,
    buttonWidth: PropTypes.number,
    disabled: PropTypes.bool,
  },

  getDefaultProps: function () {
    return {
      disabled: false,
      rowID: -1,
      sectionID: -1,
      sensitivity: 50,
    };
  },

  getInitialState: function () {
    return {
      autoClose: this.props.autoClose || false,
      btnWidth: 0,
      btnsLeftWidth: 0,
      btnsRightWidth: 0,
      contentHeight: 0,
      contentPos: 0,
      contentWidth: 0,
      openedRight: false,
      swiping: false,
      tweenDuration: 160,
      timeStart: null,
    };
  },

  componentWillMount: function () {
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: (event, gestureState) => true,
      onStartShouldSetPanResponderCapture: (event, gestureState) =>
        this.state.openedLeft || this.state.openedRight,
      onMoveShouldSetPanResponderCapture: (event, gestureState) =>
        Math.abs(gestureState.dx) > this.props.sensitivity &&
        Math.abs(gestureState.dy) <= this.props.sensitivity,
      onPanResponderGrant: this._handlePanResponderGrant,
      onPanResponderMove: this._handlePanResponderMove,
      onPanResponderRelease: this._handlePanResponderEnd,
      onPanResponderTerminate: this._handlePanResponderEnd,
      onShouldBlockNativeResponder: (event, gestureState) => false,
      onPanResponderTerminationRequest: () => false,
    });
  },

  componentWillReceiveProps: function (nextProps) {
    if (nextProps.close) this._close();
    if (nextProps.openRight) this._openRight();
    if (nextProps.openLeft) this._openLeft();
  },

  _handlePanResponderGrant: function (e: Object, gestureState: Object) {
    if (this.props.disabled) return;
    if (!this.state.openedLeft && !this.state.openedRight) {
      this._callOnOpen();
    } else {
      this._callOnClose();
    }
    this.swipeoutContent.measure((ox, oy, width, height) => {
      let buttonWidth = this.props.buttonWidth || (width / 5);
      this.setState({
        btnWidth: buttonWidth,
        btnsLeftWidth: this.props.left ? buttonWidth * this.props.left.length : 0,
        btnsRightWidth: this.props.right ? buttonWidth * this.props.right.length : 0,
        swiping: true,
        timeStart: (new Date()).getTime(),
      });
    });
  },

  _handlePanResponderMove: function (e: Object, gestureState: Object) {
    if (this.props.disabled) return;
    var posX = gestureState.dx;
    var posY = gestureState.dy;
    var leftWidth = this.state.btnsLeftWidth;
    var rightWidth = this.state.btnsRightWidth;
    if (this.state.openedRight) var posX = gestureState.dx - rightWidth;
    else if (this.state.openedLeft) var posX = gestureState.dx + leftWidth;

    //  prevent scroll if moveX is true
    var moveX = Math.abs(posX) > Math.abs(posY);
    if (this.props.scroll) {
      if (moveX) this.props.scroll(false);
      else this.props.scroll(true);
    }
    if (this.state.swiping) {
      //  move content to reveal swipeout
      if (posX < 0 && this.props.right) {
        this.setState({ contentPos: Math.min(posX, 0) })
      } else if (posX > 0 && this.props.left) {
        this.setState({ contentPos: Math.max(posX, 0) })
      };
    }
  },

  _handlePanResponderEnd: function (e: Object, gestureState: Object) {
    if (this.props.disabled) return;
    var posX = gestureState.dx;
    var contentPos = this.state.contentPos;
    var contentWidth = this.state.contentWidth;
    var btnsLeftWidth = this.state.btnsLeftWidth;
    var btnsRightWidth = this.state.btnsRightWidth;

    //  minimum threshold to open swipeout
    var openX = contentWidth * 0.33;

    //  should open swipeout
    var openLeft = posX > openX || posX > btnsLeftWidth / 2;
    var openRight = posX < -openX || posX < -btnsRightWidth / 2;

    //  account for open swipeouts
    if (this.state.openedRight) var openRight = posX - openX < -openX;
    if (this.state.openedLeft) var openLeft = posX + openX > openX;

    //  reveal swipeout on quick swipe
    var timeDiff = (new Date()).getTime() - this.state.timeStart < 200;
    if (timeDiff) {
      var openRight = posX < -openX / 10 && !this.state.openedLeft;
      var openLeft = posX > openX / 10 && !this.state.openedRight;
    }

    if (this.state.swiping) {
      if (openRight && contentPos < 0 && posX < 0) {
        this._open(-btnsRightWidth, 'right');
      } else if (openLeft && contentPos > 0 && posX > 0) {
        this._open(btnsLeftWidth, 'left');
      } else {
        this._close();
      }
    }

    //  Allow scroll
    if (this.props.scroll) this.props.scroll(true);
  },

  _tweenContent: function (state, endValue) {
    this.tweenState(state, {
      easing: tweenState.easingTypes.easeInOutQuad,
      duration: endValue === 0 ? this.state.tweenDuration * 1.5 : this.state.tweenDuration,
      endValue: endValue,
    });
  },

  _rubberBandEasing: function (value, limit) {
    if (value < 0 && value < limit) return limit - Math.pow(limit - value, 0.85);
    else if (value > 0 && value > limit) return limit + Math.pow(value - limit, 0.85);
    return value;
  },

  //  close swipeout on button press
  _autoClose: function (btn) {
    if (this.state.autoClose) this._close();
    var onPress = btn.onPress;
    if (onPress) onPress();
  },

  _open: function (contentPos, direction) {
    const left = direction === 'left';
    const { sectionID, rowID, onOpen } = this.props;
    onOpen && onOpen(sectionID, rowID, direction);
    this._tweenContent('contentPos', contentPos);
    this.setState({
      contentPos,
      openedLeft: left,
      openedRight: !left,
      swiping: false,
    });
  },

  _close: function () {
    const { sectionID, rowID, onClose } = this.props;
    if (onClose && (this.state.openedLeft || this.state.openedRight)) {
      const direction = this.state.openedRight ? 'right' : 'left';
      onClose(sectionID, rowID, direction);
    }
    this._tweenContent('contentPos', 0);
    this._callOnClose();
    this.setState({
      openedRight: false,
      openedLeft: false,
      swiping: false,
    });
  },

  _callOnClose: function () {
    if (this.props.onClose) this.props.onClose(this.props.sectionID, this.props.rowID);
  },

  _callOnOpen: function () {
    if (this.props.onOpen) this.props.onOpen(this.props.sectionID, this.props.rowID);
  },

  _openRight: function () {
    this.swipeoutContent.measure((ox, oy, width, height) => {
      let btnWidth = this.props.buttonWidth || (width / 5);

      this.setState({
        btnWidth,
        btnsRightWidth: this.props.right ? btnWidth * this.props.right.length : 0,
      }, () => {
        this._tweenContent('contentPos', -this.state.btnsRightWidth);
        this._callOnOpen();
        this.setState({
          contentPos: -this.state.btnsRightWidth,
          openedLeft: false,
          openedRight: true,
          swiping: false
        });
      });
    });
  },

  _openLeft: function () {
    this.swipeoutContent.measure((ox, oy, width, height) => {
      let btnWidth = this.props.buttonWidth || (width / 5);

      this.setState({
        btnWidth,
        btnsLeftWidth: this.props.left ? btnWidth * this.props.left.length : 0,
      }, () => {
        this._tweenContent('contentPos', this.state.btnsLeftWidth);
        this._callOnOpen();
        this.setState({
          contentPos: this.state.btnsLeftWidth,
          openedLeft: true,
          openedRight: false,
          swiping: false
        });
      });
    });
  },

  render: function () {
    var contentWidth = this.state.contentWidth;
    var posX = this.getTweeningValue('contentPos');

    var styleSwipeout = [styles.swipeout, this.props.style];
    if (this.props.backgroundColor) {
      styleSwipeout.push([{ backgroundColor: this.props.backgroundColor }]);
    }

    var limit = -this.state.btnsRightWidth;
    if (posX > 0) var limit = this.state.btnsLeftWidth;

    var styleLeftPos = {
      left: {
        left: 0,
        overflow: 'hidden',
        width: Math.min(limit * (posX / limit), limit),
      },
    };
    var styleRightPos = {
      right: {
        left: Math.abs(contentWidth + Math.max(limit, posX)),
        right: 0,
      },
    };
    var styleContentPos = {
      content: {
        transform: [{ translateX: this._rubberBandEasing(posX, limit) }],
      },
    };

    var styleContent = [styles.swipeoutContent];
    styleContent.push(styleContentPos.content);

    var styleRight = [styles.swipeoutBtns];
    styleRight.push(styleRightPos.right);

    var styleLeft = [styles.swipeoutBtns];
    styleLeft.push(styleLeftPos.left);

    var isRightVisible = posX < 0;
    var isLeftVisible = posX > 0;

    return (
      <View style={styleSwipeout}>
        <View
          ref={node => this.swipeoutContent = node}
          style={styleContent}
          onLayout={this._onLayout}
          {...this._panResponder.panHandlers}
        >
          {this.props.children}
        </View>
        {this._renderButtons(this.props.right, isRightVisible, styleRight)}
        {this._renderButtons(this.props.left, isLeftVisible, styleLeft)}
      </View>
    );
  },

  _onLayout: function (event) {
    var { width, height } = event.nativeEvent.layout;
    this.setState({
      contentWidth: width,
      contentHeight: height,
    });
  },

  _renderButtons: function (buttons, isVisible, style) {
    if (buttons && isVisible) {
      return (<View style={style}>
        {buttons.map(this._renderButton)}
      </View>);
    } else {
      return (
        <View />
      );
    }
  },

  _renderButton: function (btn, i) {
    return (
      <SwipeoutBtn
        backgroundColor={btn.backgroundColor}
        color={btn.color}
        component={btn.component}
        disabled={btn.disabled}
        height={this.state.contentHeight}
        key={i}
        onPress={() => this._autoClose(btn)}
        text={btn.text}
        type={btn.type}
        underlayColor={btn.underlayColor}
        width={this.state.btnWidth}
      />
    );
  }
})

Swipeout.NativeButton = NativeButton;
Swipeout.SwipeoutButton = SwipeoutBtn;

export default Swipeout;
<|endoftext|>


<commit_before>function defaultEqualityCheck(a, b) {
  return a === b
}

export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
  let lastArgs = null
  let lastResult = null
  return (...args) => {
    if (lastArgs !== null &&
        args.every((value, index) => equalityCheck(value, lastArgs[index]))) {
      return lastResult
    }
    lastArgs = args
    lastResult = func(...args)
    return lastResult
  }
}

function getDependencies(funcs) {
  const dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs

  if (!dependencies.every(dep => typeof dep === 'function')) {
    const dependencyTypes = dependencies.map(
      dep => typeof dep
    ).join(', ')
    throw new Error(
      `Selector creators expect all input-selectors to be functions, ` +
        `instead received the following types: [${dependencyTypes}]`
    )
  }

  return dependencies
}

export function createSelectorCreator(memoize, ...memoizeOptions) {
  return (...funcs) => {
    let recomputations = 0
    const resultFunc = funcs.pop()
    const dependencies = getDependencies(funcs)

    const memoizedResultFunc = memoize(
      (...args) => {
        recomputations++
        return resultFunc(...args)
      },
      ...memoizeOptions
    )

    const selector = (state, props, ...args) => {
      const params = dependencies.map(
        dependency => dependency(state, props, ...args)
      )
      return memoizedResultFunc(...params)
    }

    selector.recomputations = () => recomputations
    return selector
  }
}

export function createSelector(...args) {
  return createSelectorCreator(defaultMemoize)(...args)
}

export function createStructuredSelector(selectors, selectorCreator = createSelector) {
  if (typeof selectors !== 'object') {
    throw new Error(
      `createStructuredSelector expects first argument to be an object ` +
        `where each property is a selector, instead received a ${typeof selectors}`
    )
  }
  const objectKeys = Object.keys(selectors)
  return selectorCreator(
    objectKeys.map(key => selectors[key]),
    (...values) => {
      return values.reduce((composition, value, index) => {
        composition[objectKeys[index]] = value
        return composition
      }, {})
    }
  )
}
<commit_msg>Line up multiple line comments<commit_after>function defaultEqualityCheck(a, b) {
  return a === b
}

export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
  let lastArgs = null
  let lastResult = null
  return (...args) => {
    if (lastArgs !== null &&
        args.every((value, index) => equalityCheck(value, lastArgs[index]))) {
      return lastResult
    }
    lastArgs = args
    lastResult = func(...args)
    return lastResult
  }
}

function getDependencies(funcs) {
  const dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs

  if (!dependencies.every(dep => typeof dep === 'function')) {
    const dependencyTypes = dependencies.map(
      dep => typeof dep
    ).join(', ')
    throw new Error(
      `Selector creators expect all input-selectors to be functions, ` +
      `instead received the following types: [${dependencyTypes}]`
    )
  }

  return dependencies
}

export function createSelectorCreator(memoize, ...memoizeOptions) {
  return (...funcs) => {
    let recomputations = 0
    const resultFunc = funcs.pop()
    const dependencies = getDependencies(funcs)

    const memoizedResultFunc = memoize(
      (...args) => {
        recomputations++
        return resultFunc(...args)
      },
      ...memoizeOptions
    )

    const selector = (state, props, ...args) => {
      const params = dependencies.map(
        dependency => dependency(state, props, ...args)
      )
      return memoizedResultFunc(...params)
    }

    selector.recomputations = () => recomputations
    return selector
  }
}

export function createSelector(...args) {
  return createSelectorCreator(defaultMemoize)(...args)
}

export function createStructuredSelector(selectors, selectorCreator = createSelector) {
  if (typeof selectors !== 'object') {
    throw new Error(
      `createStructuredSelector expects first argument to be an object ` +
      `where each property is a selector, instead received a ${typeof selectors}`
    )
  }
  const objectKeys = Object.keys(selectors)
  return selectorCreator(
    objectKeys.map(key => selectors[key]),
    (...values) => {
      return values.reduce((composition, value, index) => {
        composition[objectKeys[index]] = value
        return composition
      }, {})
    }
  )
}
<|endoftext|>


<commit_before>var vec2 = require("vec2"),
    createPool = require("create_pool");

module.exports = Contact2;

function Contact2() {
    this.normal = vec2.create();
    this.point = vec2.create();
    this.depth = 0;
}
createPool(Contact2);

Contact2.create = function() {
    return Contact2.getPooled();
};

Contact2.prototype.destroy = function() {
    return Contact2.release(this);
};
<commit_msg>updates<commit_after>var vec2 = require("vec2"),
    createPool = require("create_pool");

module.exports = Contact2;

function Contact2() {
    this.normal = vec2.create(0, 1);
    this.point = vec2.create(0, 0);
    this.depth = 0;
}
createPool(Contact2);

Contact2.create = function() {
    return Contact2.getPooled();
};

Contact2.prototype.destroy = function() {
    return Contact2.release(this);
};

Contact2.prototype.destructor = function() {
    vec2.set(this.normal, 0, 1);
    vec2.set(this.point, 0, 0);
    this.depth = 0;
};
<|endoftext|>


<commit_before>import { createAction } from 'redux-actions';

export function createActionThunk (type, fn) {
  const TYPE_START = `${type}_STARTED`;
  const TYPE_FAIL = `${type}_FAILED`;
  const TYPE_SUCCESS = `${type}_ENDED`;
  const actionCreators = {
    [TYPE_START]   : createAction(TYPE_START),
    [TYPE_SUCCESS] : createAction(TYPE_SUCCESS),
    [TYPE_FAIL]    : createAction(TYPE_FAIL)
  };

  return (...args) => (dispatch) => {
    dispatch(actionCreators[TYPE_START](args));
    let result;
    try {
      result = fn(...args);
    } catch (error) {
      dispatch(actionCreators[TYPE_FAIL](error));
      throw error;
    }
    if (result && result.then && result.catch) {
      return result
        .then((data) => {
          dispatch(actionCreators[TYPE_SUCCESS](data));
          return data;
        }, (err) => {
          dispatch(actionCreators[TYPE_FAIL](err));
          throw err;
        });
    }
    dispatch(actionCreators[TYPE_SUCCESS](result));
    return result;
  }
}
<commit_msg>pass state and optional extra along with thunk<commit_after>import { createAction } from 'redux-actions';

export function createActionThunk (type, fn) {
  const TYPE_START = `${type}_STARTED`;
  const TYPE_FAIL = `${type}_FAILED`;
  const TYPE_SUCCESS = `${type}_ENDED`;
  const actionCreators = {
    [TYPE_START]   : createAction(TYPE_START),
    [TYPE_SUCCESS] : createAction(TYPE_SUCCESS),
    [TYPE_FAIL]    : createAction(TYPE_FAIL)
  };

  return (...args) => (dispatch, getState, extra) => {
    dispatch(actionCreators[TYPE_START](args));
    let result;
    try {
      result = fn(...args, {getState, extra});
    } catch (error) {
      dispatch(actionCreators[TYPE_FAIL](error));
      throw error;
    }
    if (result && result.then && result.catch) {
      return result
        .then((data) => {
          dispatch(actionCreators[TYPE_SUCCESS](data));
          return data;
        }, (err) => {
          dispatch(actionCreators[TYPE_FAIL](err));
          throw err;
        });
    }
    dispatch(actionCreators[TYPE_SUCCESS](result));
    return result;
  }
}
<|endoftext|>