<commit_before>'use strict';

const _      = require('lodash');
const path   = require('path');
const lodash = require('lodash');
const glob   = require('glob');
const moment = require('moment');

const debug = require('debug')('skyer');

const utils = require('./utils');

const ConfigManager    = require('./manager/ConfigManager');
const ComponentManager = require('./manager/ComponentManager');

const MiddlewaresListParser = require('./modules/MiddlewaresListParser');

const FILE_TYPE = {
  CONFIG: 'config',
  CONTROLLER: 'controller',
  CONSTANT: 'constant',
  ROUTE_MIDDLEWARE: 'route_middleware',
  APP_MIDDLEWARE: 'app_middleware',
  SYS_MIDDLEWARE: 'sys_middleware',
  ROUTE: 'route',
  SERVICE: 'service',
  VIEW: 'view'
};

class Skyer {
  constructor( options ) {
    this.options = lodash.extend(Skyer.defaultConfig, options || {});

    this._config      = {};
    this._consts      = {};
    this._controllers = {};
    this._services    = {};
    this._routes      = {};

    this.componentManager = null;

    this._route_middlewares = {};

    // map for index
    this._sys_app_middlewares_map = {};

    this._app_middlewares = [];
    this._sys_middlewares = [];

    // custom how middlewares register by config.
    this._is_custom_middlewares    = false;
    this._custom_middlewares_items = [];

    this._settings = {};

    this._listen_port     = null;
    this._listen_callback = () => {

    };

    this._listen_default_callback = () => {
      if ( this._listen_callback && typeof this._listen_callback === 'function' ) {
        this._listen_callback.call(this);
      }

      const port = this.app.port;

      debug('skyer app listen on port %s', port);
      this.logger.info('skyer app listen on port %s', port);
    };
  }

  set( key, value, attath ) {
    this._settings[key] = value;
    if ( attath ) {
      Object.defineProperty(this, key, value);
    }

    return this;
  }

  get( key ) {
    return this._settings[key];
  }

  getEnv( name ) {
    return process.env[name];
  }

  // todo: plugin api for skyer ecosystem
  plugin() {

  }

  listen( port, callback ) {
    if ( typeof port === 'function' ) {
      callback = port;
      port     = null;
    }

    this._listen_port = port;

    if ( callback && typeof  callback === 'function' ) {
      this._listen_callback = callback;
    }
  }

  fly() {
    debug('loading config...');
    this._loadConfig();

    debug('loading globals...');
    this._loadGlobals();

    debug('loading component...');
    this._loadComponents();

    debug('loading constants...');
    this._loadConstants();

    debug('loading middlewares...');
    this._loadMiddlewares();
    debug('loading controllers...');
    this._loadControllers();
    debug('loading services...');
    this._loadServices();
    debug('loading routes...');
    this._loadRoutes();

    debug('load finish!');

    debug('starting app...');
    this._start();
  }

  _loadGlobals() {
    const globalsMap = {
      _: lodash,
      moment: moment,
      skyer: this
    };

    const globalsConf = this.config.get('globals');
    if ( !globalsConf ) {
      debug('none globals config');
      return;
    }

    Object.keys(globalsMap).forEach(key => {
      if ( globalsConf[key] ) {
        global[key] = globalsMap[key];
      }
    });
  }

  _loadMiddlewares() {
    this._loadByType(FILE_TYPE.SYS_MIDDLEWARE, this._loadSysMiddleware);
    this._loadByType(FILE_TYPE.APP_MIDDLEWARE, this._loadAppMiddleware);
    this._loadByType(FILE_TYPE.ROUTE_MIDDLEWARE, this._loadRouteMiddleware);

    this._registerAppMiddlewareByConfig();
  }

  _registerAppMiddlewareByConfig() {
    const middlewaresConf = this.config.get('middlewares');
    if ( !middlewaresConf ) {
      debug('none middlewares config, default register all middlewares');
      return;
    }

    if ( typeof middlewaresConf !== 'object' ) {
      debug('invalid middlewares config, default register all middlewares');
      return;
    }

    if ( middlewaresConf.default ) {
      debug('middlewares config to default, default register all middlewares');
      return;
    }

    debug('find middlewares config, parse it!');

    this._is_custom_middlewares = true;

    // parse middlewares list
    const parser = new MiddlewaresListParser(middlewaresConf.list);
    parser.setSkyer(this).build();

    this._custom_middlewares_items = parser.getResults();
  }

  _loadSysMiddleware( prefix, Middleware, file ) {
    let name = this.utils.parseMiddleware(Middleware);
    if ( !name ) {
      debug('invalid sys Middleware file: %s', file);
      return;
    }

    const inst = new Middleware();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    this._sys_middlewares.push(inst);
    this._sys_app_middlewares_map[inst.name] = inst;
  }

  _loadAppMiddleware( prefix, Middleware, file ) {
    let name = this.utils.parseMiddleware(Middleware);
    if ( !name ) {
      debug('invalid app Middleware file: %s', file);
      return;
    }

    const inst = new Middleware();

    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    this._app_middlewares.push(inst);
    this._sys_app_middlewares_map[inst.name] = inst;
  }

  _loadRouteMiddleware( prefix, Middleware, file ) {
    let name = this.utils.parseMiddleware(Middleware);
    if ( !name ) {

      debug('invalid route Middleware file: %s', file);
      return;
    }

    // prefix /  /v1
    const inst = new Middleware();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    if ( !this._route_middlewares[prefix] ) {
      this._route_middlewares[prefix] = {};
    }

    this._route_middlewares[prefix][name] = inst;
  }

  _loadControllers() {
    this._loadByType(FILE_TYPE.CONTROLLER, this._loadController);
  }

  _loadController( prefix, Controller, file ) {
    let name = this.utils.parseController(Controller);
    if ( !name ) {

      debug('invalid controller file: %s', file);
      return;
    }

    const inst = new Controller();

    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    if ( !this._controllers[prefix] ) {
      this._controllers[prefix] = {};
    }

    this._controllers[prefix][name] = inst;
  }

  _loadServices() {
    this._loadByType(FILE_TYPE.SERVICE, this._loadService);
  }

  _loadService( prefix, Service, file ) {
    let name = this.utils.parseService(Service);
    if ( !name ) {
      debug('invalid service file: %s', file);
      return;
    }

    const inst = new Service();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this);

    // prefix /  or /v1 or /v1/test
    const prefixArr = prefix.split('/');

    let currNode = this._services;
    for ( let i = 1; i < prefixArr.length; i++ ) {
      let ns = prefixArr[i];

      if ( ns === '' ) {
        currNode[name] = inst;
      } else {
        if ( !currNode[ns] ) {
          currNode[ns] = {};
        }

        currNode = currNode[ns];

        if ( i === prefixArr.length - 1 ) {
          currNode[name] = inst;
        }
      }
    }
  }

  _loadRoutes() {
    this._loadByType(FILE_TYPE.ROUTE, this._loadRoute);
  }

  _loadRoute( prefix, Route, file ) {
    let name = this.utils.parseRoute(Route);
    if ( !name ) {
      debug('invalid route file: %s', file);
      return;
    }

    const inst = new Route();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    if ( !this._routes[prefix] ) {
      this._routes[prefix] = {};
    }

    this._routes[prefix][name] = inst;
  }

  _loadConfig() {
    const confPath = this.options.config_path;

    const configManager = new ConfigManager(confPath);
    configManager.setSkyer(this).build();

    this._config = configManager;
  }

  _loadConstants() {
    this._loadByType(FILE_TYPE.CONSTANT, this._loadConstant);
  }

  _loadConstant( prefix, Constant, file ) {
    const key = this.utils.parseConstKeyByFile(file);

    const prefixArr = prefix.split('/');

    let currNode = this._consts;
    for ( let i = 1; i < prefixArr.length; i++ ) {
      let ns = prefixArr[i];

      if ( ns === '' ) {
        currNode[key] = Constant;
      } else {
        if ( !currNode[ns] ) {
          currNode[ns] = {};
        }

        currNode = currNode[ns];

        if ( i === prefixArr.length - 1 ) {
          currNode[key] = Constant;
        }
      }
    }
  }

  _loadComponents() {
    const componentManager = new ComponentManager();
    this.componentManager  = componentManager;
    componentManager.setSkyer(this).build();
  }

  _loadByType( fileType, handler ) {
    handler = handler || function () {
      };

    let dirPath = '';
    let files   = '';
    switch ( fileType ) {
      case FILE_TYPE.ROUTE:
        dirPath = this.options.route_path;
        files   = glob.sync(dirPath + '/**/*.route.js');
        if ( !files || files.length === 0 ) {
          // load default routes
          dirPath = path.join(__dirname, 'routes');
          files   = glob.sync(dirPath + '/**/*.route.js');
        }
        break;
      case FILE_TYPE.CONTROLLER:
        dirPath = this.options.controller_path;
        files   = glob.sync(dirPath + '/**/*.controller.js');
        if ( !files || files.length === 0 ) {
          // load default controllers
          dirPath = path.join(__dirname, 'controllers');
          files   = glob.sync(dirPath + '/**/*.controller.js');
        }
        break;
      case FILE_TYPE.SYS_MIDDLEWARE:
        dirPath = path.join(__dirname, 'middlewares');
        files   = glob.sync(dirPath + '/*.js');
        break;
      case FILE_TYPE.ROUTE_MIDDLEWARE:
        dirPath = this.options.route_middleware_path;
        files   = glob.sync(dirPath + '/**/*.middleware.js');
        break;
      case FILE_TYPE.APP_MIDDLEWARE:
        dirPath = this.options.app_middleware_path;
        files   = glob.sync(dirPath + '/**/*.middleware.js');
        break;
      case FILE_TYPE.CONSTANT:
        dirPath = this.options.constant_path;
        files   = glob.sync(dirPath + '/**/*.const.js');
        break;
      case FILE_TYPE.SERVICE:
        dirPath = this.options.service_path;
        files   = glob.sync(dirPath + '/**/*.service.js');
        break;
      case FILE_TYPE.CONFIG:
      case FILE_TYPE.VIEW:
      default:
        return;
    }

    files.forEach(file => {
      let Module = this.utils.requireSafe(file);
      if ( Module ) {
        let prefix = this.utils.getPrefix(file, dirPath);
        handler.bind(this)(prefix, Module, file);
      }
    });
  }

  _start() {
    const Application = require('./Application');
    const app         = new Application(this);
    app.start();
  }

  static get defaultConfig() {
    return {
      config_path: path.join(Skyer.rootPath, 'config'),
      route_path: path.join(Skyer.rootPath, 'app/routes'),
      model_path: path.join(Skyer.rootPath, 'app/models'),
      bootstrap_path: path.join(Skyer.rootPath, 'app/bootstrap'),
      controller_path: path.join(Skyer.rootPath, 'app/controllers'),
      service_path: path.join(Skyer.rootPath, 'app/services'),
      route_middleware_path: path.join(Skyer.rootPath, 'app/middlewares/route'),
      app_middleware_path: path.join(Skyer.rootPath, 'app/middlewares/app'),
      constant_path: path.join(Skyer.rootPath, 'app/constants'),
      component_path: path.join(Skyer.rootPath, 'app/components'),

      logger_path: path.join(Skyer.rootPath, 'logs'),
      public_path: path.join(Skyer.rootPath, 'public'),
      view_path: path.join(Skyer.rootPath, 'app/views'),

      controller_prefix_separator: '^',
      controller_action_separator: '#',
      controller_action_suffix: '',
      controller_default_action: 'index',

      middleware_prefix_separator: '^',
      middleware_action_separator: '#',
      middleware_action_suffix: '',
      middleware_default_action: '__default'
    };
  }

  static get rootPath() {
    return Skyer.getEnv(Skyer.ROOT_PATH_NAME) || global[Skyer.ROOT_PATH_NAME] || process.cwd();
  }

  static get ROOT_PATH_NAME() {
    return 'SKYER_ROOT_PATH';
  }

  static getEnv( name ) {
    return process.env[name];
  }

  get utils() {
    return utils;
  }

  get env() {
    return this.getEnv('NODE_ENV') || 'development';
  }

  get controllers() {
    return this._controllers;
  }

  get services() {
    return this._services;
  }

  get config() {
    return this._config;
  }

  get consts() {
    return this._consts;
  }

  get logger() {
    // todo:
    //  skyer.logger.info('hello')
    //  skyer.logger.orderLogger.info('orderNo')
    return this.componentManager.getComponent('logger');
  }
}

Skyer._     = lodash;
Skyer.utils = utils;

Skyer.Route           = require('./abstract/AbstractRoute');
Skyer.Service         = require('./abstract/AbstractService');
Skyer.Controller      = require('./abstract/AbstractController');
Skyer.Component       = require('./abstract/AbstractComponent');
Skyer.AppMiddleware   = require('./abstract/AppMiddleware');
Skyer.RouteMiddleware = require('./abstract/RouteMiddleware');

module.exports = global.Skyer = Skyer;
require('pkginfo')(module, 'name', 'version', 'author');

<commit_msg>add Skyer static properties<commit_after>'use strict';

const _      = require('lodash');
const path   = require('path');
const lodash = require('lodash');
const glob   = require('glob');
const moment = require('moment');

const debug = require('debug')('skyer');

const utils = require('./utils');

const ConfigManager    = require('./manager/ConfigManager');
const ComponentManager = require('./manager/ComponentManager');

const MiddlewaresListParser = require('./modules/MiddlewaresListParser');

const FILE_TYPE = {
  CONFIG: 'config',
  CONTROLLER: 'controller',
  CONSTANT: 'constant',
  ROUTE_MIDDLEWARE: 'route_middleware',
  APP_MIDDLEWARE: 'app_middleware',
  SYS_MIDDLEWARE: 'sys_middleware',
  ROUTE: 'route',
  SERVICE: 'service',
  VIEW: 'view'
};

class Skyer {
  constructor( options ) {
    this.options = lodash.extend(Skyer.defaultConfig, options || {});

    this._config      = {};
    this._consts      = {};
    this._controllers = {};
    this._services    = {};
    this._routes      = {};

    this.componentManager = null;

    this._route_middlewares = {};

    // map for index
    this._sys_app_middlewares_map = {};

    this._app_middlewares = [];
    this._sys_middlewares = [];

    // custom how middlewares register by config.
    this._is_custom_middlewares    = false;
    this._custom_middlewares_items = [];

    this._settings = {};

    this._listen_port     = null;
    this._listen_callback = () => {

    };

    this._listen_default_callback = () => {
      if ( this._listen_callback && typeof this._listen_callback === 'function' ) {
        this._listen_callback.call(this);
      }

      const port = this.app.port;

      debug('skyer app listen on port %s', port);
      this.logger.info('skyer app listen on port %s', port);
    };
  }

  set( key, value, attath ) {
    this._settings[key] = value;
    if ( attath ) {
      Object.defineProperty(this, key, value);
    }

    return this;
  }

  get( key ) {
    return this._settings[key];
  }

  getEnv( name ) {
    return process.env[name];
  }

  // todo: plugin api for skyer ecosystem
  plugin() {

  }

  listen( port, callback ) {
    if ( typeof port === 'function' ) {
      callback = port;
      port     = null;
    }

    this._listen_port = port;

    if ( callback && typeof  callback === 'function' ) {
      this._listen_callback = callback;
    }
  }

  fly() {
    debug('loading config...');
    this._loadConfig();

    debug('loading globals...');
    this._loadGlobals();

    debug('loading component...');
    this._loadComponents();

    debug('loading constants...');
    this._loadConstants();

    debug('loading middlewares...');
    this._loadMiddlewares();
    debug('loading controllers...');
    this._loadControllers();
    debug('loading services...');
    this._loadServices();
    debug('loading routes...');
    this._loadRoutes();

    debug('load finish!');

    debug('starting app...');
    this._start();
  }

  _loadGlobals() {
    const globalsMap = {
      _: lodash,
      moment: moment,
      skyer: this
    };

    const globalsConf = this.config.get('globals');
    if ( !globalsConf ) {
      debug('none globals config');
      return;
    }

    Object.keys(globalsMap).forEach(key => {
      if ( globalsConf[key] ) {
        global[key] = globalsMap[key];
      }
    });
  }

  _loadMiddlewares() {
    this._loadByType(FILE_TYPE.SYS_MIDDLEWARE, this._loadSysMiddleware);
    this._loadByType(FILE_TYPE.APP_MIDDLEWARE, this._loadAppMiddleware);
    this._loadByType(FILE_TYPE.ROUTE_MIDDLEWARE, this._loadRouteMiddleware);

    this._registerAppMiddlewareByConfig();
  }

  _registerAppMiddlewareByConfig() {
    const middlewaresConf = this.config.get('middlewares');
    if ( !middlewaresConf ) {
      debug('none middlewares config, default register all middlewares');
      return;
    }

    if ( typeof middlewaresConf !== 'object' ) {
      debug('invalid middlewares config, default register all middlewares');
      return;
    }

    if ( middlewaresConf.default ) {
      debug('middlewares config to default, default register all middlewares');
      return;
    }

    debug('find middlewares config, parse it!');

    this._is_custom_middlewares = true;

    // parse middlewares list
    const parser = new MiddlewaresListParser(middlewaresConf.list);
    parser.setSkyer(this).build();

    this._custom_middlewares_items = parser.getResults();
  }

  _loadSysMiddleware( prefix, Middleware, file ) {
    let name = this.utils.parseMiddleware(Middleware);
    if ( !name ) {
      debug('invalid sys Middleware file: %s', file);
      return;
    }

    const inst = new Middleware();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    this._sys_middlewares.push(inst);
    this._sys_app_middlewares_map[inst.name] = inst;
  }

  _loadAppMiddleware( prefix, Middleware, file ) {
    let name = this.utils.parseMiddleware(Middleware);
    if ( !name ) {
      debug('invalid app Middleware file: %s', file);
      return;
    }

    const inst = new Middleware();

    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    this._app_middlewares.push(inst);
    this._sys_app_middlewares_map[inst.name] = inst;
  }

  _loadRouteMiddleware( prefix, Middleware, file ) {
    let name = this.utils.parseMiddleware(Middleware);
    if ( !name ) {

      debug('invalid route Middleware file: %s', file);
      return;
    }

    // prefix /  /v1
    const inst = new Middleware();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    if ( !this._route_middlewares[prefix] ) {
      this._route_middlewares[prefix] = {};
    }

    this._route_middlewares[prefix][name] = inst;
  }

  _loadControllers() {
    this._loadByType(FILE_TYPE.CONTROLLER, this._loadController);
  }

  _loadController( prefix, Controller, file ) {
    let name = this.utils.parseController(Controller);
    if ( !name ) {

      debug('invalid controller file: %s', file);
      return;
    }

    const inst = new Controller();

    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    if ( !this._controllers[prefix] ) {
      this._controllers[prefix] = {};
    }

    this._controllers[prefix][name] = inst;
  }

  _loadServices() {
    this._loadByType(FILE_TYPE.SERVICE, this._loadService);
  }

  _loadService( prefix, Service, file ) {
    let name = this.utils.parseService(Service);
    if ( !name ) {
      debug('invalid service file: %s', file);
      return;
    }

    const inst = new Service();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this);

    // prefix /  or /v1 or /v1/test
    const prefixArr = prefix.split('/');

    let currNode = this._services;
    for ( let i = 1; i < prefixArr.length; i++ ) {
      let ns = prefixArr[i];

      if ( ns === '' ) {
        currNode[name] = inst;
      } else {
        if ( !currNode[ns] ) {
          currNode[ns] = {};
        }

        currNode = currNode[ns];

        if ( i === prefixArr.length - 1 ) {
          currNode[name] = inst;
        }
      }
    }
  }

  _loadRoutes() {
    this._loadByType(FILE_TYPE.ROUTE, this._loadRoute);
  }

  _loadRoute( prefix, Route, file ) {
    let name = this.utils.parseRoute(Route);
    if ( !name ) {
      debug('invalid route file: %s', file);
      return;
    }

    const inst = new Route();
    inst.name ? ( name = inst.name) : ( inst.name = name);
    inst.setSkyer(this).build();

    if ( !inst.isEnabled() ) {
      // todo: logger
      return;
    }

    if ( !this._routes[prefix] ) {
      this._routes[prefix] = {};
    }

    this._routes[prefix][name] = inst;
  }

  _loadConfig() {
    const confPath = this.options.config_path;

    const configManager = new ConfigManager(confPath);
    configManager.setSkyer(this).build();

    this._config = configManager;
  }

  _loadConstants() {
    this._loadByType(FILE_TYPE.CONSTANT, this._loadConstant);
  }

  _loadConstant( prefix, Constant, file ) {
    const key = this.utils.parseConstKeyByFile(file);

    const prefixArr = prefix.split('/');

    let currNode = this._consts;
    for ( let i = 1; i < prefixArr.length; i++ ) {
      let ns = prefixArr[i];

      if ( ns === '' ) {
        currNode[key] = Constant;
      } else {
        if ( !currNode[ns] ) {
          currNode[ns] = {};
        }

        currNode = currNode[ns];

        if ( i === prefixArr.length - 1 ) {
          currNode[key] = Constant;
        }
      }
    }
  }

  _loadComponents() {
    const componentManager = new ComponentManager();
    this.componentManager  = componentManager;
    componentManager.setSkyer(this).build();
  }

  _loadByType( fileType, handler ) {
    handler = handler || function () {
      };

    let dirPath = '';
    let files   = '';
    switch ( fileType ) {
      case FILE_TYPE.ROUTE:
        dirPath = this.options.route_path;
        files   = glob.sync(dirPath + '/**/*.route.js');
        if ( !files || files.length === 0 ) {
          // load default routes
          dirPath = path.join(__dirname, 'routes');
          files   = glob.sync(dirPath + '/**/*.route.js');
        }
        break;
      case FILE_TYPE.CONTROLLER:
        dirPath = this.options.controller_path;
        files   = glob.sync(dirPath + '/**/*.controller.js');
        if ( !files || files.length === 0 ) {
          // load default controllers
          dirPath = path.join(__dirname, 'controllers');
          files   = glob.sync(dirPath + '/**/*.controller.js');
        }
        break;
      case FILE_TYPE.SYS_MIDDLEWARE:
        dirPath = path.join(__dirname, 'middlewares');
        files   = glob.sync(dirPath + '/*.js');
        break;
      case FILE_TYPE.ROUTE_MIDDLEWARE:
        dirPath = this.options.route_middleware_path;
        files   = glob.sync(dirPath + '/**/*.middleware.js');
        break;
      case FILE_TYPE.APP_MIDDLEWARE:
        dirPath = this.options.app_middleware_path;
        files   = glob.sync(dirPath + '/**/*.middleware.js');
        break;
      case FILE_TYPE.CONSTANT:
        dirPath = this.options.constant_path;
        files   = glob.sync(dirPath + '/**/*.const.js');
        break;
      case FILE_TYPE.SERVICE:
        dirPath = this.options.service_path;
        files   = glob.sync(dirPath + '/**/*.service.js');
        break;
      case FILE_TYPE.CONFIG:
      case FILE_TYPE.VIEW:
      default:
        return;
    }

    files.forEach(file => {
      let Module = this.utils.requireSafe(file);
      if ( Module ) {
        let prefix = this.utils.getPrefix(file, dirPath);
        handler.bind(this)(prefix, Module, file);
      }
    });
  }

  _start() {
    const Application = require('./Application');
    const app         = new Application(this);
    app.start();
  }

  static get defaultConfig() {
    return {
      config_path: path.join(Skyer.rootPath, 'config'),
      route_path: path.join(Skyer.rootPath, 'app/routes'),
      model_path: path.join(Skyer.rootPath, 'app/models'),
      bootstrap_path: path.join(Skyer.rootPath, 'app/bootstrap'),
      controller_path: path.join(Skyer.rootPath, 'app/controllers'),
      service_path: path.join(Skyer.rootPath, 'app/services'),
      route_middleware_path: path.join(Skyer.rootPath, 'app/middlewares/route'),
      app_middleware_path: path.join(Skyer.rootPath, 'app/middlewares/app'),
      constant_path: path.join(Skyer.rootPath, 'app/constants'),
      component_path: path.join(Skyer.rootPath, 'app/components'),

      logger_path: path.join(Skyer.rootPath, 'logs'),
      public_path: path.join(Skyer.rootPath, 'public'),
      view_path: path.join(Skyer.rootPath, 'app/views'),

      controller_prefix_separator: '^',
      controller_action_separator: '#',
      controller_action_suffix: '',
      controller_default_action: 'index',

      middleware_prefix_separator: '^',
      middleware_action_separator: '#',
      middleware_action_suffix: '',
      middleware_default_action: '__default'
    };
  }

  static get rootPath() {
    return Skyer.getEnv(Skyer.ROOT_PATH_NAME) || global[Skyer.ROOT_PATH_NAME] || process.cwd();
  }

  static get ROOT_PATH_NAME() {
    return 'SKYER_ROOT_PATH';
  }

  static getEnv( name ) {
    return process.env[name];
  }

  get utils() {
    return utils;
  }

  get env() {
    return this.getEnv('NODE_ENV') || 'development';
  }

  get controllers() {
    return this._controllers;
  }

  get services() {
    return this._services;
  }

  get config() {
    return this._config;
  }

  get consts() {
    return this._consts;
  }

  get logger() {
    // todo:
    //  skyer.logger.info('hello')
    //  skyer.logger.orderLogger.info('orderNo')
    return this.componentManager.getComponent('logger');
  }
}

Skyer._      = lodash;
Skyer.utils  = utils;
Skyer.glob   = glob;
Skyer.moment = moment;

Skyer.Route           = require('./abstract/AbstractRoute');
Skyer.Service         = require('./abstract/AbstractService');
Skyer.Controller      = require('./abstract/AbstractController');
Skyer.Component       = require('./abstract/AbstractComponent');
Skyer.AppMiddleware   = require('./abstract/AppMiddleware');
Skyer.RouteMiddleware = require('./abstract/RouteMiddleware');

module.exports = global.Skyer = Skyer;
require('pkginfo')(module, 'name', 'version', 'author');

<|endoftext|>


<commit_before>'use strict';
/**
 * Value is a wrapper for form value and associated metadata such as form
 * validaton and dirtyness state.
 *
 * @copyright Prometheus Research, LLC 2014
 * @preventMungle
 */

var Immutable                   = require('immutable');
var {List, Map, is, fromJS}     = Immutable; // jshint ignore:line
var emptyFunction               = require('./emptyFunction');
var {ScalarNode, CompositeNode} = require('./schema');
var invariant                   = require('./invariant');
var ValidationResult            = require('./ValidationResult');
var defaultValue                = require('./defaultValue');

var EMPTY_MAP = Map(); // jshint ignore:line

var DIRTY_SENTINEL = '__react_forms_dirty__';
var DIRTY = EMPTY_MAP.set(DIRTY_SENTINEL, true);

class Value {

  constructor(attributes, onUpdate, root, keyPath) {
    this.attributes = attributes;
    this.onUpdate = onUpdate;
    this.__root = root;
    this.keyPath = keyPath || [];
  }

  equals(other) {
    return other && is(this.attributes, other.attributes);
  }

  hashCode() {
    return this.attributes.hashCode();
  }

  get root() {
    return this.__root();
  }

  get key() {
    if (this.keyPath.length > 0) {
      return this.keyPath[this.keyPath.length - 1];
    } else {
      return null;
    }
  }

  get value() {
    return this.attributes.get('value');
  }

  /**
   * Schema related attributes
   */
  get abstractNode() {
    return this.attributes.get('abstractNode');
  }

  get node() {
    return this.attributes.get('node');
  }

  /**
   * Validation related attributes
   */
  get validation() {
    return this.attributes.get('validation');
  }

  get externalValidation() {
    return this.attributes.get('externalValidation');
  }

  get isValid() {
    return (
      this.attributes.get('validation').isSuccess &&
      this.attributes.get('externalValidation', ValidationResult.success()).isSuccess
    );
  }

  /**
   * Serialized value attribute
   */
  get serialized() {
    return this.attributes.get('serialized');
  }

  /**
   * Dirtyness state attributes and methods
   */
  get dirty() {
    return this.attributes.get('dirty');
  }

  makeDirty() {
    var attributes = this.attributes.setIn(['dirty', DIRTY_SENTINEL], true);
    return this.__update(attributes);
  }

  makeNotDirty() {
    var attributes = this.attributes.removeIn(['dirty', DIRTY_SENTINEL]);
    return this.__update(attributes);
  }

  get isDirty() {
    return this.attributes.get('dirty').get(DIRTY_SENTINEL, false);
  }

  get hasDirty() {
    return this.attributes.get('dirty').size > 0;
  }

  keys() {
    return this.node.keys(this.value);
  }

  get(key) {
    var abstractNode = this.node.get(key);
    var value = this.value.get(key, defaultValue(abstractNode));
    var {node, value} = abstractNode.instantiate(value);
    var externalValidation = this.externalValidation.has(key) ?
      this.externalValidation.get(key) :
      ValidationResult.success();
    var validation = this.validation.has(key) ?
      this.validation.get(key) :
      value !== undefined ?
      validate(node, value, ValidationResult.success()) :
      ValidationResult.success();
    var serialized = this.serialized.has(key) ? this.serialized.get(key) : serialize(node, value);
    var dirty = this.dirty.has(key) ?
      this.dirty.get(key) :
      this.isDirty ? DIRTY : EMPTY_MAP;
    var attributes = Map({ // jshint ignore:line
      abstractNode, node,
      value, serialized,
      validation, externalValidation,
      dirty
    });
    return new this.constructor(attributes, this.onUpdate, this.__root, this.keyPath.concat(key));
  }

  getIn(keyPath) {
    if (keyPath.length === 0) {
      return this;
    }
    var current = this;
    for (var i = 0, len = keyPath.length; i < len; i++) {
      current = current.get(keyPath[i]);
    }
    return current;
  }

  map(func) {
    var result = [];
    var iterator = this.node.keys(this.value);
    var step;
    while (!(step = iterator.next()).done) {
      result.push(func(this.get(step.value), step.value, this));
    }
    return result;
  }

  set(value) {
    value = fromJS(value);
    var {node, value} = this.abstractNode.instantiate(value);
    var attributes = {
      node, value,
      // either get a new serialized value for scalar nodes or destroy it
      // otherwise
      serialized: (node instanceof ScalarNode) ? node.serialize(value) : EMPTY_MAP
    };
    return this.__update(attributes);
  }

  transform(updater) {
    return this.set(updater(this.value));
  }

  setSerialized(serialized) {
    invariant(
      (this.node instanceof ScalarNode),
      'value should be a scalar'
    );
    var value = serialized !== '' ? this.node.deserialize(serialized) : null;
    var attributes = {value, serialized, dirty: DIRTY};
    if (value instanceof Error) {
      attributes.validation = value;
      attributes.value = serialized;
    }
    return this.__update(attributes);
  }

  setExternalValidation(externalValidation) {
    return this.__update({externalValidation});
  }

  setSchema(abstractNode) {
    var {node, value} = abstractNode.instantiate(this.value);
    return this.__update({value, node, abstractNode});
  }

  notify() {
    this.onUpdate(this);
  }

  __update(attributes) {
    var cur = this.root;
    var trace = [cur];
    for (var i = 0, len = this.keyPath.length; i < len; i++) {
      cur = cur.get(this.keyPath[i]);
      trace.push(cur);
    }

    cur = trace.pop();
    cur = cur.__with(attributes).__grow();

    while (trace.length > 0) {
      var par = trace.pop();
      cur = par.__onUpdate(cur);
    }

    return cur;
  }

  __onUpdate(child) {
    var value = this.value.set(child.key, child.value);
    var {node, value} = this.abstractNode.instantiate(value);
    if (node.constructor !== this.node.constructor) {
      value = defaultValue(node);
      return this.__with({node, value, serialized: EMPTY_MAP}).__grow();
    } else {
      var childrenValidation = child.validation.isSuccess ?
        ValidationResult.children(this.validation.children.remove(child.key)) :
        ValidationResult.children(this.validation.children.set(child.key, child.validation));
      var validation = validate(node, value, childrenValidation);
      var dirty = child.dirty.size > 0 ? this.dirty.set(child.key, child.dirty) : this.dirty.remove(child.key);
      var serialized = this.serialized.set(child.key, child.serialized);
      return this.__with({node, value, validation, dirty, serialized});
    }
  }

  __grow() {
    if (this.node instanceof CompositeNode) {
      var value = this.value.asMutable();
      var iterator = this.node.keys(this.value);
      var children = EMPTY_MAP.asMutable();

      var areChildrenValid = true;

      var step;
      while (!(step = iterator.next()).done) {
        if (
          this.value.get(step.value) !== undefined ||
          this.node.get(step.value).defaultValue !== undefined
        ) {
          var child = this.get(step.value).__grow();
          if (!child.validation.isSuccess) {
            areChildrenValid = false;
          }
          children = children.set(child.key, child);
          value = value.set(step.value, child.value)
        }
      }
      value = value.asImmutable();
      var {node, value} = this.abstractNode.instantiate(value);
      var childrenValidation = areChildrenValid ?
        ValidationResult.success() :
        new ValidationResult(null, children.map(c => c.validation));
      var validation = validate(node, value, childrenValidation);
      var dirty = this.dirty.merge(children.filter(c => c.dirty.size > 0).map(c => c.dirty));
      var serialized = this.serialized.merge(children.map(c => c.serialized));
      return this.__with({node, value, validation, dirty, serialized});
    } else {
      var validation = validate(this.node, this.value, ValidationResult.success());
      return this.__with({validation});
    }
  }

  __with(attributes) {
    attributes = this.attributes.merge(attributes);
    return new this.constructor(
      attributes,
      this.onUpdate,
      this.__root,
      this.keyPath
    );
  }

  static create(abstractNode, value, onUpdate, root) {
    onUpdate = onUpdate || emptyFunction;
    value = fromJS(value);
    if (value === undefined) {
      value = defaultValue(abstractNode);
    }
    var {node, value} = abstractNode.instantiate(value);
    var externalValidation = ValidationResult.success();
    var validation = ValidationResult.success();
    var serialized = serialize(node, value);
    var dirty = EMPTY_MAP;
    var attributes = Map({ // jshint ignore:line
      abstractNode, node,
      value, serialized,
      validation, externalValidation,
      dirty
    });
    var created = new this(attributes, onUpdate, root, null);
    created = created.__grow();
    return created;
  }

}

/**
 * Validate value against schema node.
 */
function validate(node, value, childrenValidation) {
  var result = node.validate(value, childrenValidation);
  if (result instanceof ValidationResult) {
    return result;
  } else if (result instanceof Error) {
    return new ValidationResult(result.message);
  } else {
    return ValidationResult.success();
  }
}

/**
 * Serialize value.
 */
function serialize(node, value) {
  if (node instanceof CompositeNode) {
    return EMPTY_MAP;
  } else {
    return node.serialize(value);
  }
}

module.exports = Value;
<commit_msg>Value: is(nodeType) method<commit_after>'use strict';
/**
 * Value is a wrapper for form value and associated metadata such as form
 * validaton and dirtyness state.
 *
 * @copyright Prometheus Research, LLC 2014
 * @preventMungle
 */

var Immutable                   = require('immutable');
var {List, Map, is, fromJS}     = Immutable; // jshint ignore:line
var emptyFunction               = require('./emptyFunction');
var {ScalarNode, CompositeNode} = require('./schema');
var invariant                   = require('./invariant');
var ValidationResult            = require('./ValidationResult');
var defaultValue                = require('./defaultValue');

var EMPTY_MAP = Map(); // jshint ignore:line

var DIRTY_SENTINEL = '__react_forms_dirty__';
var DIRTY = EMPTY_MAP.set(DIRTY_SENTINEL, true);

class Value {

  constructor(attributes, onUpdate, root, keyPath) {
    this.attributes = attributes;
    this.onUpdate = onUpdate;
    this.__root = root;
    this.keyPath = keyPath || [];
  }

  is(nodeType) {
    return (this.node instanceof nodeType);
  }

  equals(other) {
    return other && is(this.attributes, other.attributes);
  }

  hashCode() {
    return this.attributes.hashCode();
  }

  get root() {
    return this.__root();
  }

  get key() {
    if (this.keyPath.length > 0) {
      return this.keyPath[this.keyPath.length - 1];
    } else {
      return null;
    }
  }

  get value() {
    return this.attributes.get('value');
  }

  /**
   * Schema related attributes
   */
  get abstractNode() {
    return this.attributes.get('abstractNode');
  }

  get node() {
    return this.attributes.get('node');
  }

  /**
   * Validation related attributes
   */
  get validation() {
    return this.attributes.get('validation');
  }

  get externalValidation() {
    return this.attributes.get('externalValidation');
  }

  get isValid() {
    return (
      this.attributes.get('validation').isSuccess &&
      this.attributes.get('externalValidation', ValidationResult.success()).isSuccess
    );
  }

  /**
   * Serialized value attribute
   */
  get serialized() {
    return this.attributes.get('serialized');
  }

  /**
   * Dirtyness state attributes and methods
   */
  get dirty() {
    return this.attributes.get('dirty');
  }

  makeDirty() {
    var attributes = this.attributes.setIn(['dirty', DIRTY_SENTINEL], true);
    return this.__update(attributes);
  }

  makeNotDirty() {
    var attributes = this.attributes.removeIn(['dirty', DIRTY_SENTINEL]);
    return this.__update(attributes);
  }

  get isDirty() {
    return this.attributes.get('dirty').get(DIRTY_SENTINEL, false);
  }

  get hasDirty() {
    return this.attributes.get('dirty').size > 0;
  }

  keys() {
    return this.node.keys(this.value);
  }

  get(key) {
    var abstractNode = this.node.get(key);
    var value = this.value.get(key, defaultValue(abstractNode));
    var {node, value} = abstractNode.instantiate(value);
    var externalValidation = this.externalValidation.has(key) ?
      this.externalValidation.get(key) :
      ValidationResult.success();
    var validation = this.validation.has(key) ?
      this.validation.get(key) :
      value !== undefined ?
      validate(node, value, ValidationResult.success()) :
      ValidationResult.success();
    var serialized = this.serialized.has(key) ? this.serialized.get(key) : serialize(node, value);
    var dirty = this.dirty.has(key) ?
      this.dirty.get(key) :
      this.isDirty ? DIRTY : EMPTY_MAP;
    var attributes = Map({ // jshint ignore:line
      abstractNode, node,
      value, serialized,
      validation, externalValidation,
      dirty
    });
    return new this.constructor(attributes, this.onUpdate, this.__root, this.keyPath.concat(key));
  }

  getIn(keyPath) {
    if (keyPath.length === 0) {
      return this;
    }
    var current = this;
    for (var i = 0, len = keyPath.length; i < len; i++) {
      current = current.get(keyPath[i]);
    }
    return current;
  }

  map(func) {
    var result = [];
    var iterator = this.node.keys(this.value);
    var step;
    while (!(step = iterator.next()).done) {
      result.push(func(this.get(step.value), step.value, this));
    }
    return result;
  }

  set(value) {
    value = fromJS(value);
    var {node, value} = this.abstractNode.instantiate(value);
    var attributes = {
      node, value,
      // either get a new serialized value for scalar nodes or destroy it
      // otherwise
      serialized: (node instanceof ScalarNode) ? node.serialize(value) : EMPTY_MAP
    };
    return this.__update(attributes);
  }

  transform(updater) {
    return this.set(updater(this.value));
  }

  setSerialized(serialized) {
    invariant(
      (this.node instanceof ScalarNode),
      'value should be a scalar'
    );
    var value = serialized !== '' ? this.node.deserialize(serialized) : null;
    var attributes = {value, serialized, dirty: DIRTY};
    if (value instanceof Error) {
      attributes.validation = value;
      attributes.value = serialized;
    }
    return this.__update(attributes);
  }

  setExternalValidation(externalValidation) {
    return this.__update({externalValidation});
  }

  setSchema(abstractNode) {
    var {node, value} = abstractNode.instantiate(this.value);
    return this.__update({value, node, abstractNode});
  }

  notify() {
    this.onUpdate(this);
  }

  __update(attributes) {
    var cur = this.root;
    var trace = [cur];
    for (var i = 0, len = this.keyPath.length; i < len; i++) {
      cur = cur.get(this.keyPath[i]);
      trace.push(cur);
    }

    cur = trace.pop();
    cur = cur.__with(attributes).__grow();

    while (trace.length > 0) {
      var par = trace.pop();
      cur = par.__onUpdate(cur);
    }

    return cur;
  }

  __onUpdate(child) {
    var value = this.value.set(child.key, child.value);
    var {node, value} = this.abstractNode.instantiate(value);
    if (node.constructor !== this.node.constructor) {
      value = defaultValue(node);
      return this.__with({node, value, serialized: EMPTY_MAP}).__grow();
    } else {
      var childrenValidation = child.validation.isSuccess ?
        ValidationResult.children(this.validation.children.remove(child.key)) :
        ValidationResult.children(this.validation.children.set(child.key, child.validation));
      var validation = validate(node, value, childrenValidation);
      var dirty = child.dirty.size > 0 ? this.dirty.set(child.key, child.dirty) : this.dirty.remove(child.key);
      var serialized = this.serialized.set(child.key, child.serialized);
      return this.__with({node, value, validation, dirty, serialized});
    }
  }

  __grow() {
    if (this.node instanceof CompositeNode) {
      var value = this.value.asMutable();
      var iterator = this.node.keys(this.value);
      var children = EMPTY_MAP.asMutable();

      var areChildrenValid = true;

      var step;
      while (!(step = iterator.next()).done) {
        if (
          this.value.get(step.value) !== undefined ||
          this.node.get(step.value).defaultValue !== undefined
        ) {
          var child = this.get(step.value).__grow();
          if (!child.validation.isSuccess) {
            areChildrenValid = false;
          }
          children = children.set(child.key, child);
          value = value.set(step.value, child.value)
        }
      }
      value = value.asImmutable();
      var {node, value} = this.abstractNode.instantiate(value);
      var childrenValidation = areChildrenValid ?
        ValidationResult.success() :
        new ValidationResult(null, children.map(c => c.validation));
      var validation = validate(node, value, childrenValidation);
      var dirty = this.dirty.merge(children.filter(c => c.dirty.size > 0).map(c => c.dirty));
      var serialized = this.serialized.merge(children.map(c => c.serialized));
      return this.__with({node, value, validation, dirty, serialized});
    } else {
      var validation = validate(this.node, this.value, ValidationResult.success());
      return this.__with({validation});
    }
  }

  __with(attributes) {
    attributes = this.attributes.merge(attributes);
    return new this.constructor(
      attributes,
      this.onUpdate,
      this.__root,
      this.keyPath
    );
  }

  static create(abstractNode, value, onUpdate, root) {
    onUpdate = onUpdate || emptyFunction;
    value = fromJS(value);
    if (value === undefined) {
      value = defaultValue(abstractNode);
    }
    var {node, value} = abstractNode.instantiate(value);
    var externalValidation = ValidationResult.success();
    var validation = ValidationResult.success();
    var serialized = serialize(node, value);
    var dirty = EMPTY_MAP;
    var attributes = Map({ // jshint ignore:line
      abstractNode, node,
      value, serialized,
      validation, externalValidation,
      dirty
    });
    var created = new this(attributes, onUpdate, root, null);
    created = created.__grow();
    return created;
  }

}

/**
 * Validate value against schema node.
 */
function validate(node, value, childrenValidation) {
  var result = node.validate(value, childrenValidation);
  if (result instanceof ValidationResult) {
    return result;
  } else if (result instanceof Error) {
    return new ValidationResult(result.message);
  } else {
    return ValidationResult.success();
  }
}

/**
 * Serialize value.
 */
function serialize(node, value) {
  if (node instanceof CompositeNode) {
    return EMPTY_MAP;
  } else {
    return node.serialize(value);
  }
}

module.exports = Value;
<|endoftext|>


<commit_before>function Value(value) {
  switch (typeof value) {
    case "number": return {N: String(value)}
    case "string": return {S: value}
  }

  if (value) switch (typeof value[0]) {
    case "number": return {NN: value.map(String)}
    case "string": return {SS: value}
  }

  throw new Error("Invalid key value type.")
}

Value.prototype = {
  parse: function(data) {
    var name = Object.keys(data)[0]
      , value = data[name]

    switch (name) {
      case "S":
      case "SS":
	return value

      case "N":
	return Number(value)

      case "NS":
	return value.map(Number)

      default:
	throw new Error("Invalid data type: " + name)
    }
  }
}

module.exports = Value
<commit_msg>formatting<commit_after>function Value(value) {
  switch (typeof value) {
    case "number": return {N: String(value)}
    case "string": return {S: value}
  }

  if (value) switch (typeof value[0]) {
    case "number": return {NN: value.map(String)}
    case "string": return {SS: value}
  }

  throw new Error("Invalid key value type.")
}

Value.prototype = {
  parse: function(data) {
    var name = Object.keys(data)[0]
      , value = data[name]

    switch (name) {
      case "S":
      case "SS":
        return value

      case "N":
        return Number(value)

      case "NS":
        return value.map(Number)

      default:
        throw new Error("Invalid data type: " + name)
    }
  }
}

module.exports = Value
<|endoftext|>


<commit_before>/**
 * @file 百度推送
 *   需要在配置文件里设定两个参数，可以在百度开发者管理平台获取
 *     secretKey:
 *     apiKey:
 *   百度推送的RESTApi参考
 *     http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/api
 * Created by luohao on 14/10/28.
 */

var light    = require("light-framework")
  , crypto   = require("crypto")
  , request  = require("request")
  , constant = require("./constant")
  , _        = light.util.underscore
  , conf     = light.util.config.push;

/**
 * 给指定的Tag发送消息
 * Tag与用户的关联在管理画面指定
 * @param tag
 * @param message
 * {
 *   title:
 *   description:
 *   custom_content: {
 *     type:
 *     notifyid:
 *   }
 * }
 * @param callback
 */

exports.pushTag = function(tag, message, callback) {

  var meta = getMessageBody("2", message);
  meta.tag = tag;
  post(meta, callback);
};

/**
 * 推送给所有人
 * @param tag
 * @param message
 * @param callback
 */
exports.pushAll = function(tag, message, callback) {

  var meta = getMessageBody("3", message);
  post(meta, callback);
};

/**
 * 推送给指定的人
 * @param userId
 * @param channelId 指定用户的特定设备
 * @param message
 * @param callback
 */
exports.push = function(userId, channelId, message, callback) {

  var meta = getMessageBody("1", message);
  meta.user_id = userId;
  if (channelId) {
    meta.channel_id = channelId;
  }

  post(meta, callback);
};

/**
 * 获取消息本体结构
 * @param type
 * @param message
 * @returns {{}}
 */
function getMessageBody(type, message) {

  var meta = {};
  meta.method = "push_msg";
  meta.apikey = conf.baidu.apiKey;
  meta.push_type = type;     // 1:单个人 2:一群人 3:所有用户
  meta.message_type = "1";  // 0:消息 1:通知
  meta.timestamp = Math.round(new Date().getTime() / 1000) + "";

  meta.messages = JSON.stringify(message);
  meta.sign = sign(message);
  meta.msg_keys = message.custom_content.notifyid;

  return meta;
}

/**
 * 获取调用参数签名值
 * @param stuff
 * @returns {*}
 */
function sign(stuff) {

  var part = "POST" + constant.PUSH_URL;
  part += _.map(_.keys(stuff).sort(), function (key) {
    return key + "=" + stuff[key];
  }).join("");
  part += conf.baidu.secretKey;

  return md5(encodeURIComponent(part))
}

function md5(stuff) {
  return crypto.createHash("md5").update(stuff, "utf8").digest("hex");
}

/**
 * 进行推送数据
 * @param data
 * @param callback
 */
function post(data, callback) {

  request.post({ url: constant.PUSH_URL, form: data }, function (err, response) {
    if (err) {
      return callback(err);
    }

    if (response.statusCode != 200) {
      return callback({code: "PUSH_ERROR", data: response});
    }

    return callback(null);
  });
}
<commit_msg>百度通知API完成<commit_after>/**
 * @file 百度推送
 *   需要在配置文件里设定两个参数，可以在百度开发者管理平台获取
 *     secretKey:
 *     apiKey:
 *   百度推送的RESTApi参考
 *     http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/api
 * Created by luohao on 14/10/28.
 */

var light    = require("light-framework")
  , crypto   = require("crypto")
  , request  = require("request")
  , constant = require("./constant")
  , _        = light.util.underscore
  , conf     = light.util.config.push;

/**
 * 给指定的Tag发送消息
 * Tag与用户的关联在管理画面指定
 * @param tag
 * @param message
 * {
 *   title:
 *   description:
 *   custom_content: {
 *     type:
 *     notifyid:
 *   }
 * }
 * @param callback
 */

exports.pushTag = function(tag, message, callback) {

  var meta = getMessageBody("2", message);
  meta.tag = tag;
  post(meta, callback);
};

/**
 * 推送给所有人
 * @param message
 * @param callback
 */
exports.pushAll = function(message, callback) {

  var meta = getMessageBody("3", message);
  post(meta, callback);
};

/**
 * 推送给指定的人
 * 用无账户系统模式在百度登陆设备，可以获取唯一的userId来识别设备
 * 这个userId与业务的ID不同，他们的关联关系需要自己维护
 * @param token 注册设备时，获得的userId，
 * @param message
 * @param callback
 */
exports.push = function(token, message, callback) {

  var meta = getMessageBody("1", message);
  meta.user_id = token;

  post(meta, callback);
};

/**
 * 获取消息本体结构
 * @param type
 * @param message
 * @returns {{}}
 */
function getMessageBody(type, message) {

  var meta = {};
  meta.method = "push_msg";
  meta.apikey = conf.baidu.apiKey;
  meta.push_type = type;     // 1:单个人 2:一群人 3:所有用户
  meta.message_type = "1";  // 0:消息 1:通知
  meta.timestamp = Math.round(new Date().getTime() / 1000) + "";

  meta.msg_keys = message.custom_content.notifyid;
  meta.messages = JSON.stringify(message);
  meta.sign = sign(meta);

  return meta;
}

/**
 * 获取调用参数签名值
 * @param stuff
 * @returns {*}
 */
function sign(stuff) {

  var part = "POST" + constant.BAIDU_PUSH_URL;
  part += _.map(_.keys(stuff).sort(), function (key) {
    return key + "=" + stuff[key];
  }).join("");
  part += conf.baidu.secretKey;

  return md5(encodeURIComponent(part));
}

function md5(stuff) {
  return crypto.createHash("md5").update(stuff, "utf8").digest("hex");
}

/**
 * 进行推送数据
 * @param data
 * @param callback
 */
function post(data, callback) {

  request.post({ url: constant.BAIDU_PUSH_URL, form: data }, function (err, response, body) {
    if (err) {
      return callback(err);
    }

    if (response.statusCode != 200) {
      return callback({code: "PUSH_ERROR", data: body});
    }

    return callback(null);
  });
}
<|endoftext|>


<commit_before>// Load modules

var Url = require('url');
var Async = require('async');
var Boom = require('boom');
var Hoek = require('hoek');

// Declare internals

var internals = {};

module.exports.config = function (settings) {

    return {
        handler: function (request, reply) {

            var resultsData = {
                results: [],
                resultsMap: []
            };

            var requests = [];
            var requestRegex = /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g;       // /project/$1.project/tasks, does not allow using array responses

            // Validate requests

            var errorMessage = null;
            var parseRequest = function ($0, $1, $2) {

                if ($1) {
                    if ($1 < i) {
                        parts.push({ type: 'ref', index: $1, value: $2 });
                        return '';
                    }

                    errorMessage = 'Request reference is beyond array size: ' + i;
                    return $0;
                }

                parts.push({ type: 'text', value: $2 });
                return '';
            };

            if (!request.payload.requests) {
                return reply(Boom.badRequest('Request missing requests array'));
            }

            for (var i = 0, il = request.payload.requests.length; i < il; ++i) {

                // Break into parts

                var parts = [];
                var result = request.payload.requests[i].path.replace(requestRegex, parseRequest);

                // Make sure entire string was processed (empty)

                if (result === '') {
                    requests.push(parts);
                }
                else {
                    errorMessage = errorMessage || 'Invalid request format in item: ' + i;
                    break;
                }
            }

            if (errorMessage === null) {
                internals.process(request, requests, resultsData, reply);
            }
            else {
                reply(Boom.badRequest(errorMessage));
            }
        },
        description: settings.description,
        auth: settings.auth,
        tags: settings.tags
    };
};

internals.process = function (request, requests, resultsData, reply) {

    var fnsParallel = [];
    var fnsSerial = [];
    var callBatch = function (pos, batchParts) {

        return function (callback) {

            internals.batch(request, resultsData, pos, batchParts, callback);
        };
    };

    for (var i = 0, il = requests.length; i < il; ++i) {
        var parts = requests[i];

        if (internals.hasRefPart(parts)) {
            fnsSerial.push(callBatch(i, parts));
        }
        else {
            fnsParallel.push(callBatch(i, parts));
        }
    }

    Async.series([
        function (callback) {

            Async.parallel(fnsParallel, callback);
        },
        function (callback) {

            Async.series(fnsSerial, callback);
        }
    ], function (err) {

        if (err) {
            reply(err);
        }
        else {
            reply(resultsData.results);
        }
    });
};

internals.hasRefPart = function (parts) {

    for (var i = 0, il = parts.length; i < il; ++i) {
        if (parts[i].type === 'ref') {
            return true;
        }
    }

    return false;
};

internals.batch = function (batchRequest, resultsData, pos, parts, callback) {

    var path = '';
    var error = null;

    for (var i = 0, il = parts.length; i < il; ++i) {
        path += '/';

        if (parts[i].type === 'ref') {
            var ref = resultsData.resultsMap[parts[i].index];

            if (ref) {
                var value = Hoek.reach(ref, parts[i].value);

                if (value !== null && value !== undefined) {

                    if (/^[\w:]+$/.test(value)) {
                        path += value;
                    }
                    else {
                        error = new Error('Reference value includes illegal characters');
                        break;
                    }
                }
                else {
                    error = new Error('Reference not found');
                    break;
                }
            }
            else {
                error = new Error('Missing reference response');
                break;
            }
        }
        else {
            path += parts[i].value;
        }
    }

    if (error === null) {

        // Make request
        batchRequest.payload.requests[pos].path = path;
        internals.dispatch(batchRequest, batchRequest.payload.requests[pos], function (data) {

            // If redirection
            if (('' + data.statusCode).indexOf('3') === 0) {
                batchRequest.payload.requests[pos].path = data.headers.location;
                internals.dispatch(batchRequest, batchRequest.payload.requests[pos], function (batchData) {

                    var batchResult = batchData.result;

                    resultsData.results[pos] = batchResult;
                    resultsData.resultsMap[pos] = batchResult;
                    callback(null, batchResult);
                });
                return;
            }

            var result = data.result;
            resultsData.results[pos] = result;
            resultsData.resultsMap[pos] = result;
            callback(null, result);
        });
    }
    else {
        resultsData.results[pos] = error;
        return callback(error);
    }
};

internals.dispatch = function (batchRequest, request, callback) {

    var path = request.path;

    if (request.query) {
        var urlObject = {
            pathname: request.path,
            query: request.query
        };
        path = Url.format(urlObject);
    }

    var body = (request.payload !== null && request.payload !== undefined ? JSON.stringify(request.payload) : null);     // payload can be '' or 0
    var injectOptions = {
        url: path,
        method: request.method,
        headers: batchRequest.headers,
        payload: body,
        session: batchRequest.session
    };

    if (batchRequest.server.connections.length == 1) {
      batchRequest.server.inject(injectOptions, callback);
    }
    else {
      batchRequest.connection.inject(injectOptions, callback);
    }
};
<commit_msg>Fix multi connections<commit_after>// Load modules

var Url = require('url');
var Async = require('async');
var Boom = require('boom');
var Hoek = require('hoek');

// Declare internals

var internals = {};

module.exports.config = function (settings) {

    return {
        handler: function (request, reply) {

            var resultsData = {
                results: [],
                resultsMap: []
            };

            var requests = [];
            var requestRegex = /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g;       // /project/$1.project/tasks, does not allow using array responses

            // Validate requests

            var errorMessage = null;
            var parseRequest = function ($0, $1, $2) {

                if ($1) {
                    if ($1 < i) {
                        parts.push({ type: 'ref', index: $1, value: $2 });
                        return '';
                    }

                    errorMessage = 'Request reference is beyond array size: ' + i;
                    return $0;
                }

                parts.push({ type: 'text', value: $2 });
                return '';
            };

            if (!request.payload.requests) {
                return reply(Boom.badRequest('Request missing requests array'));
            }

            for (var i = 0, il = request.payload.requests.length; i < il; ++i) {

                // Break into parts

                var parts = [];
                var result = request.payload.requests[i].path.replace(requestRegex, parseRequest);

                // Make sure entire string was processed (empty)

                if (result === '') {
                    requests.push(parts);
                }
                else {
                    errorMessage = errorMessage || 'Invalid request format in item: ' + i;
                    break;
                }
            }

            if (errorMessage === null) {
                internals.process(request, requests, resultsData, reply);
            }
            else {
                reply(Boom.badRequest(errorMessage));
            }
        },
        description: settings.description,
        auth: settings.auth,
        tags: settings.tags
    };
};

internals.process = function (request, requests, resultsData, reply) {

    var fnsParallel = [];
    var fnsSerial = [];
    var callBatch = function (pos, batchParts) {

        return function (callback) {

            internals.batch(request, resultsData, pos, batchParts, callback);
        };
    };

    for (var i = 0, il = requests.length; i < il; ++i) {
        var parts = requests[i];

        if (internals.hasRefPart(parts)) {
            fnsSerial.push(callBatch(i, parts));
        }
        else {
            fnsParallel.push(callBatch(i, parts));
        }
    }

    Async.series([
        function (callback) {

            Async.parallel(fnsParallel, callback);
        },
        function (callback) {

            Async.series(fnsSerial, callback);
        }
    ], function (err) {

        if (err) {
            reply(err);
        }
        else {
            reply(resultsData.results);
        }
    });
};

internals.hasRefPart = function (parts) {

    for (var i = 0, il = parts.length; i < il; ++i) {
        if (parts[i].type === 'ref') {
            return true;
        }
    }

    return false;
};

internals.batch = function (batchRequest, resultsData, pos, parts, callback) {

    var path = '';
    var error = null;

    for (var i = 0, il = parts.length; i < il; ++i) {
        path += '/';

        if (parts[i].type === 'ref') {
            var ref = resultsData.resultsMap[parts[i].index];

            if (ref) {
                var value = Hoek.reach(ref, parts[i].value);

                if (value !== null && value !== undefined) {

                    if (/^[\w:]+$/.test(value)) {
                        path += value;
                    }
                    else {
                        error = new Error('Reference value includes illegal characters');
                        break;
                    }
                }
                else {
                    error = new Error('Reference not found');
                    break;
                }
            }
            else {
                error = new Error('Missing reference response');
                break;
            }
        }
        else {
            path += parts[i].value;
        }
    }

    if (error === null) {

        // Make request
        batchRequest.payload.requests[pos].path = path;
        internals.dispatch(batchRequest, batchRequest.payload.requests[pos], function (data) {

            // If redirection
            if (('' + data.statusCode).indexOf('3') === 0) {
                batchRequest.payload.requests[pos].path = data.headers.location;
                internals.dispatch(batchRequest, batchRequest.payload.requests[pos], function (batchData) {

                    var batchResult = batchData.result;

                    resultsData.results[pos] = batchResult;
                    resultsData.resultsMap[pos] = batchResult;
                    callback(null, batchResult);
                });
                return;
            }

            var result = data.result;
            resultsData.results[pos] = result;
            resultsData.resultsMap[pos] = result;
            callback(null, result);
        });
    }
    else {
        resultsData.results[pos] = error;
        return callback(error);
    }
};

internals.dispatch = function (batchRequest, request, callback) {

    var path = request.path;

    if (request.query) {
        var urlObject = {
            pathname: request.path,
            query: request.query
        };
        path = Url.format(urlObject);
    }

    var body = (request.payload !== null && request.payload !== undefined ? JSON.stringify(request.payload) : null);     // payload can be '' or 0
    var injectOptions = {
        url: path,
        method: request.method,
        headers: batchRequest.headers,
        payload: body,
        session: batchRequest.session
    };

    if (batchRequest.server.connections.length == 1) {
      batchRequest.server.inject(injectOptions, callback);
    }
    else {
      batchRequest.connection.inject(injectOptions, callback);
    }
};
<|endoftext|>


<commit_before>/**
 * @overview Book and Pages Generator Lib
 * @module books
 * @author Dominik Sigmund
 * @version 0.1
 * @description This Lib is the new way to create Pages and Books.
 * @memberof resilienz
 * @requires module:fs
 * @requires module:path
 * @requires module:child_process
 * @requires module:fs-copy-file-sync
 * @requires bin:imagemagick
 */
var fs = require('fs')
var path = require('path')
const execSync = require('child_process').execSync
const copyFileSync = require('fs-copy-file-sync')
function Books (database, layoutpath, pagespath, imagepath, bookpath) {
  var self = {}
  self.database = database
  self.layoutpath = layoutpath
  self.pagespath = pagespath
  self.imagepath = imagepath
  self.bookpath = bookpath
  self.createPage = createPage
  self.getPages = getPages
  self.printTextField = printTextField
  self.addImage = addImage
  self.printNumber = printNumber
  self.printAllNumbers = printAllNumbers
  self.removePage = removePage
  self.createIndex = createIndex
  self.copyFixed = copyFixed
  self.combinePages = combinePages
  self.makeBook = makeBook
  self.getBook = getBook
  self.rescaleImage = rescaleImage
  return self
}

function createPage (actionid, category, page, language, callback) { // -> creates one page
  var self = this
  self.database.getLayoutInfo(actionid, page, function (error, objects) {
    if (error) {
      return callback(error)
    } else {
      if (objects.length === 0) {
        self.removePage(actionid, ('0' + category).substring(2) + '-' + ('0' + page).substring(2))
        callback(null, true)
      } else {
        var background = path.join(self.layoutpath, language, objects[0].background)
        var target = path.join(self.pagespath, actionid) + ('0' + category).substring(2) + '-tmp' + ('0' + page).substring(2) + '.png'
        copyFileSync(background, target)
        for (let index = 0; index < objects.length; index++) {
          const object = objects[index]
          if (object.type === 'image' && object.value !== '') {
            self.addImage(target, object)
          } else if (object.type === 'text' && object.value !== '') { // text
            self.printTextField(target, object)
          }
        }
        // split page, save second as page+1
        execSync('convert ' + target + ' -crop 2127x3072+0+0 ' + path.join(self.pagespath, actionid) + ('0' + category).substring(2) + '-' + ('0' + page).substring(2) + '.png')
        execSync('convert ' + target + ' -crop 2127x3072+2128+0 ' + path.join(self.pagespath, actionid) + ('0' + category).substring(2) + '-' + ('0' + (page + 1)).substring(2) + '.png')
        fs.unlinkSync(target)
        callback(null, true)
      }
    }
  })
}
function printTextField (target, object) { // -> adds text to page
  // FIXME: make better
  var text = object.value.split('|')[1]
                // remove 10% from width / height and add half value to x / y for centering
  var w10 = object.width / 10
  var h10 = object.height / 10
  var wnew = object.width - w10
  var hnew = object.height - h10
  var nx = object.x - (w10 / 2)
  var ny = object.y - (h10 / 2)
  var cmd = 'convert -font Helvetica -pointsize 200 -background white -fill \'#1e487a\' -gravity northwest -geometry \'' + wnew + 'x' + hnew + '!+' + nx + '+' + ny + '\' caption:"' + text + '" ' + target + ' +swap -gravity northwest -composite  ' + target
  console.log(cmd)
  var output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    return false
  } else {
    return true
  }
}
function addImage (target, object) { // -> add image to page
  var imgname = object.value
  object.value = this.imagepath + '/' + object.value
  var rscmd = 'convert ' + object.value + ' -resize ' + object.width + 'x' + object.height + ' -background white -compose Copy -gravity center -extent ' + object.width + 'x' + object.height + ' /tmp/' + imgname
  console.log(rscmd)
  var output = execSync(rscmd, true)
  object.value = '/tmp/' + imgname
  var cmd = ''
  if (object.spin !== 0) {
    cmd = 'convert ' + target + ' null: \\( ' + object.value + ' -sample ' + object.width + 'x' + object.height + '! -background None -rotate ' + object.spin + ' \\) -geometry \'+' + object.x + '+' + object.y + '\' -layers Composite ' + target
  } else {
    cmd = 'composite -geometry \'' + object.width + 'x' + object.height + '!+' + object.x + '+' + object.y + '\' ' + object.value + ' ' + target + ' ' + target
  }
  console.log(cmd)
  output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    return false
  } else {
    return true
  }
}
function printNumber (actionid, page, realNumber) { // -> prints number on page
  var target = path.join(this.pagespath, actionid) + ('0' + page.cat).substring(2) + '-' + ('0' + page.page).substring(2) + '.png'
  var cmd1 = 'convert ' + target + ' -font Helvetica -pointsize 24 -gravity southwest -stroke \'#000C\' -strokewidth 2 -annotate +50+10 \'' + realNumber + '\' -stroke none -fill white -annotate +50+10 \'' + realNumber + '\' ' + target
  var output1 = execSync(cmd1, true)
  if (output1.stderr || output1.stdout) {
    return output1.stderr || output1.stdout
  } else {
    return null
  }
}
function printAllNumbers (actionid, pages, callback) { // -> call printNumber for all pages
  for (let index = 0; index < pages.length; index++) {
    const page = pages[index]
    if (page.cat !== 0 && page.cat !== 2 && page.cat !== 99) {
      this.printNumber(actionid, page, page.realNumber)
    }
  }
}
function removePage (actionid, page, callback) { // -> removes a page from the build
  fs.unlink(path.join(path.join(this.pagespath, actionid, page) + '.png'), callback)
}
function createIndex (actionid, language, pages, callback) { //  -> print indexpage (3) 02-01.png
  var self = this
  self.database.getCatNamesByLanguage(language, function (error, translations) {
    if (error) {
      callback(error)
    } else {
      fs.copyFile(path.join(self.layoutpath + '/' + language + '/02_inhalt.png'), path.join(self.pagespath + '/' + actionid + '/02-01.png'), function (error) {
        if (error) {
          callback(error)
        } else {
          var startY = 30
          for (let index = 1; index < translations.length; index++) {
            const chapter = translations[index]
            if (chapter.id === 2) {
              self.printTextField(path.join(self.pagespath + '/' + actionid + '/02-01.png'), { // use printTextField to print first chapter to 02_01.png
                width: 100,
                height: 200,
                x: 10,
                y: 10,
                value: '11|' + chapter.translation + ' ... 2'
              })
            } else {
              var startpage = pages.find(x => x.cat === chapter.id)[0].realNumber
              self.printTextField(path.join(self.pagespath + '/' + actionid + '/02-01.png'), { // use printTextField to print  chapter-list to 02_01.png
                width: 100,
                height: 200,
                x: 10,
                y: startY,
                value: '11|' + chapter.translation + ' ... ' + startpage
              })
              startY += 10
            }
          }
        }
      })
    }
  })
}
function getPages (actionid, language, callback) { //  -> get All Pages from Folder
  fs.readdir(path.join(this.imagepath + actionid), function (error, files) {
    if (error) {
      callback(error)
    } else {
      var pages = []
      for (let index = 0; index < files.length; index++) {
        const file = files[index]
        if (path.extname(file) === 'png') {
          var name = path.basename(file, '.png')
          var nsplit = name.split('-')
          var po = {
            page: parseInt(nsplit[1]),
            cat: parseInt(nsplit[0]),
            realNumber: -1,
            path: path.join(this.imagepath + actionid + file)
          }
          pages.push(po)
        }
      }
      // sort by cat, page.
      pages.sort(function (a, b) {
        if (a.cat < b.cat) {
          return -1
        } else if (a.cat === b.cat) {
          if (a.page < b.page) {
            return -1
          } else if (a.page > b.page) {
            return 1
          } else { // a.page == b.page (should never happen)
            return 0
          }
        } else { // a.cat > b.cat
          return 1
        }
      })
      // create realNumber (loop, not for cats 00, 02, 99)
      var count = 1
      for (let index = 0; index < pages.length; index++) {
        const page = pages[index]
        if (page.cat !== '00' && page.cat !== '02' && page.cat !== '99') {
          page.realNumber = count
          count++
        }
      }
      callback(null, pages)
    }
  })
}
function copyFixed (actionid, language, callback) { // -> copies pages 2 and the last into the folder
  fs.copyFile(path.join(this.layoutpath + '/' + language + '/02_vorwort.png'), path.join(this.pagespath + '/' + actionid + '/02-00.png'), function (error) {
    if (error) {
      callback(error)
    } else {
      fs.copyFile(path.join(this.layoutpath + '/' + language + '/16_ruecken.png'), path.join(this.pagespath + '/' + actionid + '/99-00.png'), function (error) {
        if (error) {
          callback(error)
        } else {
          callback(null)
        }
      })
    }
  })
}
function combinePages (actionid, pages, callback) { // -> Combines Pages
  var pagesString = ''
  for (let index = 0; index < pages.length; index++) {
    const page = pages[index]
    pagesString += page.path + ' '
  }
  var cmd = 'convert ' + pagesString + ' -density 27.8x28.2 -page a4 -compress Zip ' + this.bookpath + '/' + actionid + '.pdf'
  console.log(cmd)
  var output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    callback(output)
  } else {
    callback(null, this.bookpath + '/' + actionid + '.pdf')
  }
}
function makeBook (actionid, language, callback) { // -> creates Book
  var self = this
  self.copyFixed(actionid, language, function (error) {
    if (error) {
      callback(error)
    } else {
      self.getPages(actionid, language, function (error, pages) {
        if (error) {
          callback(error)
        } else {
          self.createIndex(actionid, language, pages, function (error) {
            if (error) {
              callback(error)
            } else {
              self.printAllNumbers(actionid, pages, function (error) {
                if (error) {
                  callback(error)
                } else {
                  self.combinePages(actionid, pages, function (error, pdf) {
                    if (error) {
                      callback(error)
                    } else {
                      callback(null, pdf)
                    }
                  })
                }
              })
            }
          })
        }
      })
    }
  })
}
function getBook (actionid, callback) { // -> just send book
  var self = this
  fs.access(self.bookpath + '/' + actionid + '.pdf', fs.constants.R_OK | fs.constants.W_OK, function (error) {
    if (error) {
      callback(error)
    } else {
      callback(null, self.bookpath + '/' + actionid + '.pdf')
    }
  })
}
function rescaleImage (image, x, y, width, height, clientWidth, clientHeight, orgWidth, orgHeight, callback) { // -> rescales Images and saves it.
  var newWidth = (width * orgWidth) / clientWidth
  var newHeight = (width * orgHeight) / clientHeight
  var newX = (x * orgWidth) / clientWidth
  var newY = (y * orgHeight) / clientHeight
  var cmd = 'convert ' + image + ' -crop ' + newWidth + 'x' + newHeight + '' + '+' + newX + '+' + newY + ' ' + image
  console.log(cmd)
  var output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    callback(output)
  } else {
    callback(null)
  }
}

module.exports = Books
<commit_msg>filenaming problems<commit_after>/**
 * @overview Book and Pages Generator Lib
 * @module books
 * @author Dominik Sigmund
 * @version 0.1
 * @description This Lib is the new way to create Pages and Books.
 * @memberof resilienz
 * @requires module:fs
 * @requires module:path
 * @requires module:child_process
 * @requires module:fs-copy-file-sync
 * @requires bin:imagemagick
 */
var fs = require('fs')
var path = require('path')
const execSync = require('child_process').execSync
const copyFileSync = require('fs-copy-file-sync')
function Books (database, layoutpath, pagespath, imagepath, bookpath) {
  var self = {}
  self.database = database
  self.layoutpath = layoutpath
  self.pagespath = pagespath
  self.imagepath = imagepath
  self.bookpath = bookpath
  self.createPage = createPage
  self.getPages = getPages
  self.printTextField = printTextField
  self.addImage = addImage
  self.printNumber = printNumber
  self.printAllNumbers = printAllNumbers
  self.removePage = removePage
  self.createIndex = createIndex
  self.copyFixed = copyFixed
  self.combinePages = combinePages
  self.makeBook = makeBook
  self.getBook = getBook
  self.rescaleImage = rescaleImage
  return self
}

function createPage (actionid, category, page, language, callback) { // -> creates one page
  var self = this
  self.database.getLayoutInfo(actionid, page, function (error, objects) {
    if (error) {
      return callback(error)
    } else {
      if (objects.length === 0) {
        self.removePage(actionid, ('0' + category).substring(2) + '-' + ('0' + page).substring(2))
        callback(null, true)
      } else {
        var background = path.join(self.layoutpath, language, objects[0].background)
        var target = path.join(self.pagespath, actionid) + '/' + ('0' + category).substring(2) + '-tmp' + ('0' + page).substring(2) + '.png'
        copyFileSync(background, target)
        for (let index = 0; index < objects.length; index++) {
          const object = objects[index]
          if (object.type === 'image' && object.value !== '') {
            self.addImage(target, object)
          } else if (object.type === 'text' && object.value !== '') { // text
            self.printTextField(target, object)
          } else {
            console.log(object)
          }
        }
        // split page, save second as page+1
        execSync('convert ' + target + ' -crop 2127x3072+0+0 ' + path.join(self.pagespath, actionid) + '/' + ('0' + category).substring(2) + '-' + ('0' + page).substring(2) + '.png')
        execSync('convert ' + target + ' -crop 2127x3072+2128+0 ' + path.join(self.pagespath, actionid) + '/' + ('0' + category).substring(2) + '-' + ('0' + (parseInt(page) + 1)).substring(2) + '.png')
        fs.unlinkSync(target)
        callback(null, true)
      }
    }
  })
}
function printTextField (target, object) { // -> adds text to page
  // FIXME: make better
  var text = object.value.split('|')[1]
                // remove 10% from width / height and add half value to x / y for centering
  var w10 = object.width / 10
  var h10 = object.height / 10
  var wnew = object.width - w10
  var hnew = object.height - h10
  var nx = object.x - (w10 / 2)
  var ny = object.y - (h10 / 2)
  var cmd = 'convert -font Helvetica -pointsize 200 -background white -fill \'#1e487a\' -gravity northwest -geometry \'' + wnew + 'x' + hnew + '!+' + nx + '+' + ny + '\' caption:"' + text + '" ' + target + ' +swap -gravity northwest -composite  ' + target
  console.log(cmd)
  var output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    return false
  } else {
    return true
  }
}
function addImage (target, object) { // -> add image to page
  var imgname = object.value
  object.value = this.imagepath + '/' + object.value
  var rscmd = 'convert ' + object.value + ' -resize ' + object.width + 'x' + object.height + ' -background white -compose Copy -gravity center -extent ' + object.width + 'x' + object.height + ' /tmp/' + imgname
  console.log(rscmd)
  var output = execSync(rscmd, true)
  object.value = '/tmp/' + imgname
  var cmd = ''
  if (object.spin !== 0) {
    cmd = 'convert ' + target + ' null: \\( ' + object.value + ' -sample ' + object.width + 'x' + object.height + '! -background None -rotate ' + object.spin + ' \\) -geometry \'+' + object.x + '+' + object.y + '\' -layers Composite ' + target
  } else {
    cmd = 'composite -geometry \'' + object.width + 'x' + object.height + '!+' + object.x + '+' + object.y + '\' ' + object.value + ' ' + target + ' ' + target
  }
  console.log(cmd)
  output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    return false
  } else {
    return true
  }
}
function printNumber (actionid, page, realNumber) { // -> prints number on page
  var target = path.join(this.pagespath, actionid) + '/' + ('0' + page.cat).substring(2) + '-' + ('0' + page.page).substring(2) + '.png'
  var cmd1 = 'convert ' + target + ' -font Helvetica -pointsize 24 -gravity southwest -stroke \'#000C\' -strokewidth 2 -annotate +50+10 \'' + realNumber + '\' -stroke none -fill white -annotate +50+10 \'' + realNumber + '\' ' + target
  var output1 = execSync(cmd1, true)
  if (output1.stderr || output1.stdout) {
    return output1.stderr || output1.stdout
  } else {
    return null
  }
}
function printAllNumbers (actionid, pages, callback) { // -> call printNumber for all pages
  for (let index = 0; index < pages.length; index++) {
    const page = pages[index]
    if (page.cat !== 0 && page.cat !== 2 && page.cat !== 99) {
      this.printNumber(actionid, page, page.realNumber)
    }
  }
}
function removePage (actionid, page, callback) { // -> removes a page from the build
  fs.unlink(path.join(path.join(this.pagespath, actionid, page) + '.png'), callback)
}
function createIndex (actionid, language, pages, callback) { //  -> print indexpage (3) 02-01.png
  var self = this
  self.database.getCatNamesByLanguage(language, function (error, translations) {
    if (error) {
      callback(error)
    } else {
      fs.copyFile(path.join(self.layoutpath + '/' + language + '/02_inhalt.png'), path.join(self.pagespath + '/' + actionid + '/02-01.png'), function (error) {
        if (error) {
          callback(error)
        } else {
          var startY = 30
          for (let index = 1; index < translations.length; index++) {
            const chapter = translations[index]
            if (chapter.id === 2) {
              self.printTextField(path.join(self.pagespath + '/' + actionid + '/02-01.png'), { // use printTextField to print first chapter to 02_01.png
                width: 100,
                height: 200,
                x: 10,
                y: 10,
                value: '11|' + chapter.translation + ' ... 2'
              })
            } else {
              var startpage = pages.find(x => x.cat === chapter.id)[0].realNumber
              self.printTextField(path.join(self.pagespath + '/' + actionid + '/02-01.png'), { // use printTextField to print  chapter-list to 02_01.png
                width: 100,
                height: 200,
                x: 10,
                y: startY,
                value: '11|' + chapter.translation + ' ... ' + startpage
              })
              startY += 10
            }
          }
        }
      })
    }
  })
}
function getPages (actionid, language, callback) { //  -> get All Pages from Folder
  fs.readdir(path.join(this.imagepath + actionid), function (error, files) {
    if (error) {
      callback(error)
    } else {
      var pages = []
      for (let index = 0; index < files.length; index++) {
        const file = files[index]
        if (path.extname(file) === 'png') {
          var name = path.basename(file, '.png')
          var nsplit = name.split('-')
          var po = {
            page: parseInt(nsplit[1]),
            cat: parseInt(nsplit[0]),
            realNumber: -1,
            path: path.join(this.imagepath + actionid + file)
          }
          pages.push(po)
        }
      }
      // sort by cat, page.
      pages.sort(function (a, b) {
        if (a.cat < b.cat) {
          return -1
        } else if (a.cat === b.cat) {
          if (a.page < b.page) {
            return -1
          } else if (a.page > b.page) {
            return 1
          } else { // a.page == b.page (should never happen)
            return 0
          }
        } else { // a.cat > b.cat
          return 1
        }
      })
      // create realNumber (loop, not for cats 00, 02, 99)
      var count = 1
      for (let index = 0; index < pages.length; index++) {
        const page = pages[index]
        if (page.cat !== '00' && page.cat !== '02' && page.cat !== '99') {
          page.realNumber = count
          count++
        }
      }
      callback(null, pages)
    }
  })
}
function copyFixed (actionid, language, callback) { // -> copies pages 2 and the last into the folder
  fs.copyFile(path.join(this.layoutpath + '/' + language + '/02_vorwort.png'), path.join(this.pagespath + '/' + actionid + '/02-00.png'), function (error) {
    if (error) {
      callback(error)
    } else {
      fs.copyFile(path.join(this.layoutpath + '/' + language + '/16_ruecken.png'), path.join(this.pagespath + '/' + actionid + '/99-00.png'), function (error) {
        if (error) {
          callback(error)
        } else {
          callback(null)
        }
      })
    }
  })
}
function combinePages (actionid, pages, callback) { // -> Combines Pages
  var pagesString = ''
  for (let index = 0; index < pages.length; index++) {
    const page = pages[index]
    pagesString += page.path + ' '
  }
  var cmd = 'convert ' + pagesString + ' -density 27.8x28.2 -page a4 -compress Zip ' + this.bookpath + '/' + actionid + '.pdf'
  console.log(cmd)
  var output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    callback(output)
  } else {
    callback(null, this.bookpath + '/' + actionid + '.pdf')
  }
}
function makeBook (actionid, language, callback) { // -> creates Book
  var self = this
  self.copyFixed(actionid, language, function (error) {
    if (error) {
      callback(error)
    } else {
      self.getPages(actionid, language, function (error, pages) {
        if (error) {
          callback(error)
        } else {
          self.createIndex(actionid, language, pages, function (error) {
            if (error) {
              callback(error)
            } else {
              self.printAllNumbers(actionid, pages, function (error) {
                if (error) {
                  callback(error)
                } else {
                  self.combinePages(actionid, pages, function (error, pdf) {
                    if (error) {
                      callback(error)
                    } else {
                      callback(null, pdf)
                    }
                  })
                }
              })
            }
          })
        }
      })
    }
  })
}
function getBook (actionid, callback) { // -> just send book
  var self = this
  fs.access(self.bookpath + '/' + actionid + '.pdf', fs.constants.R_OK | fs.constants.W_OK, function (error) {
    if (error) {
      callback(error)
    } else {
      callback(null, self.bookpath + '/' + actionid + '.pdf')
    }
  })
}
function rescaleImage (image, x, y, width, height, clientWidth, clientHeight, orgWidth, orgHeight, callback) { // -> rescales Images and saves it.
  var newWidth = (width * orgWidth) / clientWidth
  var newHeight = (width * orgHeight) / clientHeight
  var newX = (x * orgWidth) / clientWidth
  var newY = (y * orgHeight) / clientHeight
  var cmd = 'convert ' + image + ' -crop ' + newWidth + 'x' + newHeight + '' + '+' + newX + '+' + newY + ' ' + image
  console.log(cmd)
  var output = execSync(cmd, true)
  if (output.stderr || output.stdout) {
    console.log(output.stdout)
    console.error(output.stderr)
    callback(output)
  } else {
    callback(null)
  }
}

module.exports = Books
<|endoftext|>