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

// suport for endsWith how needs ES2015
require('string.prototype.endswith');

// Module dependencies.
var async = require('async');
var http = require('http');
var _ = require('lodash');
var path = require('path');
var wePassport = require('./auth/passport');
var localization = require('./localization');
var exec = require('child_process').exec;
var staticConfig = require('./staticConfig');
var projectPath = process.cwd();

/**
 * We.js object
 *
 * @type {Object}
 */
var we = {
  projectPath: projectPath,
  // enviroment config prod | dev | test
  env: require('./env.js'),
  // winston logger
  log: require('./log')(projectPath),
  // we.utils.async, we.utils._ ... see the ./utils file
  utils: require('./utils'),
  // hooks and events
  hooks: require('./hooks'),
  events: require('./events'),
  // database logic and models is avaible in we.db.models
  db: require('./database'),
  // Access Control List
  acl: require('./acl'),
  // we.js prototypes
  class: require('./class'),
  // start configs with static configs
  config: staticConfig(projectPath),
  // we.js sanitizer
  sanitizer: require('./sanitizer'),
  // helper functions to load we.js resources
  loaders: require('./loaders'),
  // view logic
  view: require('./view'),
  // The we.js router
  router: require('./router'),
  email: require('./email'),
  bootstrapStarted: false,
  childProcesses:[],
  // plugin manager and plugins vars
  pluginManager: require('./pluginManager'),
  plugins: {},
  pluginPaths: [],
  pluginNames: [],
  // controllers
  controllers: {},
  // 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',
  // flag to check if needs restart
  needsRestart: false
};
//Overide default toString and inspect to custom infos in we.js object
we.inspect = function inspect() {
  return '\nWe.js ;)\n';
};
we.toString = we.inspect;
// - init database
we.db.defaultConnection = we.db.connect(we.env);
//set for more compatbility with sequelize docs
we.db.sequelize = we.db.defaultConnection;
// client side config generator
we.getAppBootstrapConfig = require('./staticConfig/getAppBootstrapConfig.js');
we.antiSpam = require('./antiSpam');
// load we.js auth logic
we.auth = require('./auth');

we.view.initialize(we);
// load express and base middlewares
var weExpress = require('./express');
// save a reference to we.js object in plugin prototype
we.class.Plugin.prototype.we = we;

// -- register core bootstrap hooks
// -- this allows to unregister any step after run we.bootstrap method
//
we.hooks.on('bootstrap', [
  function loadCoreFeatures(we, next) {
    we.log.verbose('loadCoreFeatures step');

    we.db.loadCoreModels(function(err) {
      if(err) return next(err);

      we.pluginManager.loadPluginsSettingsFromDB(we, function (err){
        if (err) return next(err);
        // preload all plugins
        we.pluginManager.loadPlugins(we, function (err, plugins) {
          if (err) return next(err);
          we.plugins = plugins;
          next();
        });
      });
    });
  }, function loadPluginFeatures(we, next) {
    we.log.verbose('loadPluginFeatures step');

    we.pluginNames = we.pluginManager.pluginNames;
    // load plugin static configs, merge with old we.config and
    // override the defalt config
    we.config = staticConfig.loadPluginConfigs();

    async.eachSeries(we.pluginNames, function (pluginName, next) {
      we.plugins[pluginName].loadFeatures(we, next);
    }, function (err) {
      if (err) return next(err);
      // load plugin widgets
      for (var widgetName in we.view.configuration.widgets) {
        we.view.widgets[widgetName] =
          require(we.view.configuration.widgets[widgetName])(we.projectPath, we.class.Widget);
      }

      we.events.emit('we:after:load:plugins', we);
      next();
    })
  }, function checkRequirements(we, next) {
    we.log.verbose('checkRequirements step');

    we.hooks.trigger('we:check:requirements', we, function (err) {
      if (err) return next(err);
      exec('gm version', function (error) {
        if (error) {
          return next('Requirement GraphicsMagick not found or not instaled, see: https://github.com/aheckmann/gm');
        }
        return next();
      });
    });
  }, function loadModels(we, next) {
    we.log.verbose('loadModels step');
    we.loaders.loadModels(we, function(err, models) {
      if (err) return next(err);
      _.merge(we.db.modelsConfigs, models);
      next();
    })
  }, function instantiateModels(we, next) {
    we.log.verbose('instantiateModels step');
    we.hooks.trigger('we:models:before:instance', we, function (err) {
      if (err)  return next(err);
      var haveAlias;

      for ( var modelName in we.db.modelsConfigs) {
        haveAlias = we.router.alias.modelHaveUrlAlias(we.db.modelsConfigs[modelName]);

        if (haveAlias) {
          // add url alias virtual field
          we.db.modelsConfigs[modelName].definition.urlPath = {
            type: we.db.Sequelize.VIRTUAL,
            formFieldType: null,
            get: function() {
              if (this.cachedUrlPathAlias) return this.cachedUrlPathAlias;
              this.cachedUrlPathAlias = this.getUrlPathAlias()
              return this.cachedUrlPathAlias;
            }
          };
          // field for set alias
          we.db.modelsConfigs[modelName].definition.setAlias = {
            type: we.db.Sequelize.VIRTUAL,
            formFieldType: null
          };
        }

        // all models have a link permanent
        we.db.modelsConfigs[modelName].definition.linkPermanent = {
          type: we.db.Sequelize.VIRTUAL,
          formFieldType: null,
          get: function() {
            if (this.cachedLinkPermanent) return this.cachedLinkPermanent;
            this.cachedLinkPermanent = this.getUrlPath()
            return this.cachedLinkPermanent;
          }
        };

        // define the model
        we.db.models[modelName] = we.db.define(
          modelName,
          we.db.modelsConfigs[modelName].definition,
          we.db.modelsConfigs[modelName].options
        );

        if (haveAlias) {
          // add url alias hooks in all models if enable url alias
          we.db.models[modelName].addHook('afterCreate', 'createUrlAlias', we.router.alias.afterCreatedRecord);
          we.db.models[modelName].addHook('afterUpdate', 'updateUrlAlias', we.router.alias.afterUpdatedRecord);
          we.db.models[modelName].addHook('afterDestroy', 'destroyUrlAlias', we.router.alias.afterDeleteRecord);
        }
      }

      // set all associations
      we.db.setModelAllJoins();

      we.hooks.trigger('we:models:set:joins', we, function afterSetJoins(err) {
        if (err)  return next(err);
        next();
      });
    });
  }, function loadControllers(we, next) {
    we.log.verbose('loadControllers step');
    we.loaders.loadControllers(we, function(err, controllers) {
      if (err) return next(err);
      _.merge(we.controllers, controllers);

      we.events.emit('we:after:load:controllers', we);
      next();
    })
  }, function installAndRegisterPlugins(we, next) {
    if (we.config.skipInstall) return next();

    we.log.verbose('installAndRegisterPluginsIfNeed step');
    // dont have plugins to install
    if (!we.pluginManager.pluginsToInstall) return next();
    // sync all model before start the install scripts
    we.db.defaultConnection.sync().then(function() {
      // get plugins to install names
      var names = Object.keys(we.pluginManager.pluginsToInstall);
      async.eachSeries(names, function (name, nextPlugin) {
        // run install scripts
        we.pluginManager.installPlugin(name, function (err){
          if (err) return nextPlugin(err);
          // register it
          we.pluginManager.registerPlugin(name, nextPlugin);
        });
      }, function (err) {
        if (err) return next(err);
        next();
      });
    }).catch(next);
  },

  function setExpressApp(we, next) {
    // load express
    we.express = weExpress(we);
    we.events.emit('we:after:load:express', we);
    next();
  },

  function ACL(we, next) {
    we.log.verbose('initACL step');
    we.acl.init(we, next);
  },

  function passport(we, next) {
    we.log.verbose('initPassport step');
    // - Passports configs
    wePassport.configureAndSetStrategies(we);
    we.events.emit('we:after:load:passport', we);
    // admin env middleware
    we.express.get('/admin*', function adminPage(req ,res, next) {
      res.locals.isAdmin = true;
      res.locals.theme = we.view.adminTheme;
      res.locals.template = 'home/index';
      return next();
    });
    next();
  }, function initI18n(we, next) {
    we.log.verbose('initI18n step');
    localization(we);
    we.events.emit('we:after:init:i18n', we);
    next();
  },
  function createDefaultFolders(we, next) {
    we.log.verbose('createDefaultFolders step');
    we.hooks.trigger('we:create:default:folders', we, function() {
      next();
    });
  },

  function loadEmailTemplates(we, next) {
    we.log.verbose('loadEmailTemplates step');
    we.email.init(we);

    we.email.loadEmailTemplates(we, function (err, templates) {
      if (err) return next(err);
      we.email.templates = templates;
      next();
    });
  },

  function registerAllViewTemplates(we, next) {
    we.log.verbose('registerAllViewTemplates step');
    we.view.registerAll();
    next();
  },

  function initializeAliasFeature(we, next) {
    we.log.verbose('initializeAliasFeature step');
    we.router.alias.initialize(we, next);
  },

  function mergeRoutes(we, next) {
    we.log.verbose('mergeRoutes step');
    we.routes = {};
    // merge plugin routes
    for ( var plugin in we.plugins) {
      _.merge(we.routes, we.plugins[plugin].routes);
    }
    // merge project routes
     _.merge(we.routes, we.config.routes);
    next();
  },
  function bindRoutes(we, next) {
    we.log.verbose('bindRoutes step');
    we.hooks.trigger('we:before:routes:bind', we, function beforeRouteBind() {
      for (var route in we.routes) {
        we.router.bindRoute(we, route, we.routes[route] );
      }

      we.hooks.trigger('we:after:routes:bind', we, function afterRouteBind() {

        // bind after router handler for run responseMethod
        we.express.use(function(req, res, done) {
          if (res.responseMethod) return res[res.responseMethod]();
          done();
        });

        next();
      });
    });
  },
  function bindResources(we, next) {
    for (var resource in we.router.resources) {
      we.router.bindResource(we.router.resources[resource]);
    }
    next();
  }
]);

/**
 * Bootstrap and initialize the app
 *
 * @param  {Object}   configOnRun optional
 * @param  {Function} cb          callback to run after load we.js
 */
we.bootstrap = function bootstrap(configOnRun, cb) {
  // 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.trace();
      console.error('Error on we.js bootstrap: ', err);
      return cb(err);
    }

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

    we.log.debug('We.js bootstrap done');
    return cb(null, we);
  });
}

/**
 * Start we.js server (express)
 * use after we.bootstrap
 *
 * @param  {Function} cb    callback how returns with cb(err);
 */
we.startServer = function startExpressServer(cb) {
  we.hooks.trigger('we:server:before:start',we, function (err) {
    if (err) return cb(err);
    /**
     * Get port from environment and store in Express.
     */
    var port = normalizePort(we.config.port);
    we.express.set('port', port);

    /**
     * Create HTTP server with suport to url alias rewrite
     */
    var server;
    if (we.config.enableUrlAlias) {
      server = http.createServer(we.router.alias.httpHandler);
    } else {
      server = http.createServer(we.express);
    }

    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:', 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) {
      var 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;
      }

      var 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() {
      var addr = server.address();
      var bind = typeof addr === 'string'
        ? 'pipe ' + addr
        : 'port ' + addr.port;
      we.log.info('Run in '+we.env+' enviroment and listening on ' + bind);
    }
    // save the current http server
    we.http = server;
    // express error handlers
    // will print stacktrace
    we.express.use(function onExpressError(err, req, res) {
      we.log.error('Error on:', req.path, err);
      res.serverError();
    });

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

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

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

/**
 * Turn off process function
 *
 * @param  {Function} cb callback
 */
we.exit = function exit(cb) {
  we.db.defaultConnection.close();
  cb();
}

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

module.exports = we;
<commit_msg>add metadata field in all records to set related meta data<commit_after>/**
 * We.js main file, load we.js core and features
 */

// suport for endsWith how needs ES2015
require('string.prototype.endswith');

// Module dependencies.
var async = require('async');
var http = require('http');
var _ = require('lodash');
var path = require('path');
var wePassport = require('./auth/passport');
var localization = require('./localization');
var exec = require('child_process').exec;
var staticConfig = require('./staticConfig');
var projectPath = process.cwd();

/**
 * We.js object
 *
 * @type {Object}
 */
var we = {
  projectPath: projectPath,
  // enviroment config prod | dev | test
  env: require('./env.js'),
  // winston logger
  log: require('./log')(projectPath),
  // we.utils.async, we.utils._ ... see the ./utils file
  utils: require('./utils'),
  // hooks and events
  hooks: require('./hooks'),
  events: require('./events'),
  // database logic and models is avaible in we.db.models
  db: require('./database'),
  // Access Control List
  acl: require('./acl'),
  // we.js prototypes
  class: require('./class'),
  // start configs with static configs
  config: staticConfig(projectPath),
  // we.js sanitizer
  sanitizer: require('./sanitizer'),
  // helper functions to load we.js resources
  loaders: require('./loaders'),
  // view logic
  view: require('./view'),
  // The we.js router
  router: require('./router'),
  email: require('./email'),
  bootstrapStarted: false,
  childProcesses:[],
  // plugin manager and plugins vars
  pluginManager: require('./pluginManager'),
  plugins: {},
  pluginPaths: [],
  pluginNames: [],
  // controllers
  controllers: {},
  // 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',
  // flag to check if needs restart
  needsRestart: false
};
//Overide default toString and inspect to custom infos in we.js object
we.inspect = function inspect() {
  return '\nWe.js ;)\n';
};
we.toString = we.inspect;
// - init database
we.db.defaultConnection = we.db.connect(we.env);
//set for more compatbility with sequelize docs
we.db.sequelize = we.db.defaultConnection;
// client side config generator
we.getAppBootstrapConfig = require('./staticConfig/getAppBootstrapConfig.js');
we.antiSpam = require('./antiSpam');
// load we.js auth logic
we.auth = require('./auth');

we.view.initialize(we);
// load express and base middlewares
var weExpress = require('./express');
// save a reference to we.js object in plugin prototype
we.class.Plugin.prototype.we = we;

// -- register core bootstrap hooks
// -- this allows to unregister any step after run we.bootstrap method
//
we.hooks.on('bootstrap', [
  function loadCoreFeatures(we, next) {
    we.log.verbose('loadCoreFeatures step');

    we.db.loadCoreModels(function(err) {
      if(err) return next(err);

      we.pluginManager.loadPluginsSettingsFromDB(we, function (err){
        if (err) return next(err);
        // preload all plugins
        we.pluginManager.loadPlugins(we, function (err, plugins) {
          if (err) return next(err);
          we.plugins = plugins;
          next();
        });
      });
    });
  }, function loadPluginFeatures(we, next) {
    we.log.verbose('loadPluginFeatures step');

    we.pluginNames = we.pluginManager.pluginNames;
    // load plugin static configs, merge with old we.config and
    // override the defalt config
    we.config = staticConfig.loadPluginConfigs();

    async.eachSeries(we.pluginNames, function (pluginName, next) {
      we.plugins[pluginName].loadFeatures(we, next);
    }, function (err) {
      if (err) return next(err);
      // load plugin widgets
      for (var widgetName in we.view.configuration.widgets) {
        we.view.widgets[widgetName] =
          require(we.view.configuration.widgets[widgetName])(we.projectPath, we.class.Widget);
      }

      we.events.emit('we:after:load:plugins', we);
      next();
    })
  }, function checkRequirements(we, next) {
    we.log.verbose('checkRequirements step');

    we.hooks.trigger('we:check:requirements', we, function (err) {
      if (err) return next(err);
      exec('gm version', function (error) {
        if (error) {
          return next('Requirement GraphicsMagick not found or not instaled, see: https://github.com/aheckmann/gm');
        }
        return next();
      });
    });
  }, function loadModels(we, next) {
    we.log.verbose('loadModels step');
    we.loaders.loadModels(we, function(err, models) {
      if (err) return next(err);
      _.merge(we.db.modelsConfigs, models);
      next();
    })
  }, function instantiateModels(we, next) {
    //  step to define all models with sequelize
    we.log.verbose('instantiateModels step');
    we.hooks.trigger('we:models:before:instance', we, function (err) {
      if (err)  return next(err);
      var haveAlias;

      for ( var modelName in we.db.modelsConfigs) {
        haveAlias = we.router.alias.modelHaveUrlAlias(we.db.modelsConfigs[modelName]);

        // set alias field before define
        if (haveAlias) {
          // add url alias virtual field
          we.db.modelsConfigs[modelName].definition.urlPath = {
            type: we.db.Sequelize.VIRTUAL,
            formFieldType: null,
            get: function() {
              if (this.cachedUrlPathAlias) return this.cachedUrlPathAlias;
              this.cachedUrlPathAlias = this.getUrlPathAlias()
              return this.cachedUrlPathAlias;
            }
          };
          // field for set alias
          we.db.modelsConfigs[modelName].definition.setAlias = {
            type: we.db.Sequelize.VIRTUAL,
            formFieldType: null
          };
        }

        // all models have a link permanent
        we.db.modelsConfigs[modelName].definition.linkPermanent = {
          type: we.db.Sequelize.VIRTUAL,
          formFieldType: null,
          get: function() {
            if (this.cachedLinkPermanent) return this.cachedLinkPermanent;
            this.cachedLinkPermanent = this.getUrlPath()
            return this.cachedLinkPermanent;
          }
        };

        // set
        we.db.modelsConfigs[modelName].definition.metadata = {
          type: we.db.Sequelize.VIRTUAL,
          formFieldType: null
        };

        // define the model
        we.db.models[modelName] = we.db.define(
          modelName,
          we.db.modelsConfigs[modelName].definition,
          we.db.modelsConfigs[modelName].options
        );

        if (haveAlias) {
          // add url alias hooks in all models if enable url alias
          we.db.models[modelName].addHook('afterCreate', 'createUrlAlias', we.router.alias.afterCreatedRecord);
          we.db.models[modelName].addHook('afterUpdate', 'updateUrlAlias', we.router.alias.afterUpdatedRecord);
          we.db.models[modelName].addHook('afterDestroy', 'destroyUrlAlias', we.router.alias.afterDeleteRecord);
        }
      }

      // set all associations
      we.db.setModelAllJoins();

      we.hooks.trigger('we:models:set:joins', we, function afterSetJoins(err) {
        if (err)  return next(err);
        next();
      });
    });
  }, function loadControllers(we, next) {
    we.log.verbose('loadControllers step');
    we.loaders.loadControllers(we, function(err, controllers) {
      if (err) return next(err);
      _.merge(we.controllers, controllers);

      we.events.emit('we:after:load:controllers', we);
      next();
    })
  }, function installAndRegisterPlugins(we, next) {
    if (we.config.skipInstall) return next();

    we.log.verbose('installAndRegisterPluginsIfNeed step');
    // dont have plugins to install
    if (!we.pluginManager.pluginsToInstall) return next();
    // sync all model before start the install scripts
    we.db.defaultConnection.sync().then(function() {
      // get plugins to install names
      var names = Object.keys(we.pluginManager.pluginsToInstall);
      async.eachSeries(names, function (name, nextPlugin) {
        // run install scripts
        we.pluginManager.installPlugin(name, function (err){
          if (err) return nextPlugin(err);
          // register it
          we.pluginManager.registerPlugin(name, nextPlugin);
        });
      }, function (err) {
        if (err) return next(err);
        next();
      });
    }).catch(next);
  },

  function setExpressApp(we, next) {
    // load express
    we.express = weExpress(we);
    we.events.emit('we:after:load:express', we);
    next();
  },

  function ACL(we, next) {
    we.log.verbose('initACL step');
    we.acl.init(we, next);
  },

  function passport(we, next) {
    we.log.verbose('initPassport step');
    // - Passports configs
    wePassport.configureAndSetStrategies(we);
    we.events.emit('we:after:load:passport', we);
    // admin env middleware
    we.express.get('/admin*', function adminPage(req ,res, next) {
      res.locals.isAdmin = true;
      res.locals.theme = we.view.adminTheme;
      res.locals.template = 'home/index';
      return next();
    });
    next();
  }, function initI18n(we, next) {
    we.log.verbose('initI18n step');
    localization(we);
    we.events.emit('we:after:init:i18n', we);
    next();
  },
  function createDefaultFolders(we, next) {
    we.log.verbose('createDefaultFolders step');
    we.hooks.trigger('we:create:default:folders', we, function() {
      next();
    });
  },

  function loadEmailTemplates(we, next) {
    we.log.verbose('loadEmailTemplates step');
    we.email.init(we);

    we.email.loadEmailTemplates(we, function (err, templates) {
      if (err) return next(err);
      we.email.templates = templates;
      next();
    });
  },

  function registerAllViewTemplates(we, next) {
    we.log.verbose('registerAllViewTemplates step');
    we.view.registerAll();
    next();
  },

  function initializeAliasFeature(we, next) {
    we.log.verbose('initializeAliasFeature step');
    we.router.alias.initialize(we, next);
  },

  function mergeRoutes(we, next) {
    we.log.verbose('mergeRoutes step');
    we.routes = {};
    // merge plugin routes
    for ( var plugin in we.plugins) {
      _.merge(we.routes, we.plugins[plugin].routes);
    }
    // merge project routes
     _.merge(we.routes, we.config.routes);
    next();
  },
  function bindRoutes(we, next) {
    we.log.verbose('bindRoutes step');
    we.hooks.trigger('we:before:routes:bind', we, function beforeRouteBind() {
      for (var route in we.routes) {
        we.router.bindRoute(we, route, we.routes[route] );
      }

      we.hooks.trigger('we:after:routes:bind', we, function afterRouteBind() {

        // bind after router handler for run responseMethod
        we.express.use(function(req, res, done) {
          if (res.responseMethod) return res[res.responseMethod]();
          done();
        });

        next();
      });
    });
  },
  function bindResources(we, next) {
    for (var resource in we.router.resources) {
      we.router.bindResource(we.router.resources[resource]);
    }
    next();
  }
]);

/**
 * Bootstrap and initialize the app
 *
 * @param  {Object}   configOnRun optional
 * @param  {Function} cb          callback to run after load we.js
 */
we.bootstrap = function bootstrap(configOnRun, cb) {
  // 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.trace();
      console.error('Error on we.js bootstrap: ', err);
      return cb(err);
    }

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

    we.log.debug('We.js bootstrap done');
    return cb(null, we);
  });
}

/**
 * Start we.js server (express)
 * use after we.bootstrap
 *
 * @param  {Function} cb    callback how returns with cb(err);
 */
we.startServer = function startExpressServer(cb) {
  we.hooks.trigger('we:server:before:start',we, function (err) {
    if (err) return cb(err);
    /**
     * Get port from environment and store in Express.
     */
    var port = normalizePort(we.config.port);
    we.express.set('port', port);

    /**
     * Create HTTP server with suport to url alias rewrite
     */
    var server;
    if (we.config.enableUrlAlias) {
      server = http.createServer(we.router.alias.httpHandler);
    } else {
      server = http.createServer(we.express);
    }

    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:', 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) {
      var 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;
      }

      var 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() {
      var addr = server.address();
      var bind = typeof addr === 'string'
        ? 'pipe ' + addr
        : 'port ' + addr.port;
      we.log.info('Run in '+we.env+' enviroment and listening on ' + bind);
    }
    // save the current http server
    we.http = server;
    // express error handlers
    // will print stacktrace
    we.express.use(function onExpressError(err, req, res) {
      we.log.error('Error on:', req.path, err);
      res.serverError();
    });

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

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

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

/**
 * Turn off process function
 *
 * @param  {Function} cb callback
 */
we.exit = function exit(cb) {
  we.db.defaultConnection.close();
  cb();
}

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

module.exports = we;
<|endoftext|>


<commit_before>// Copyright 2017 SAP SE.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http: //www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific
// language governing permissions and limitations under the License.

'use strict';

var path = require('path');
var assign = require('object-assign');
var clone = require('clone');
var css = require('css');

var createParser = require('./less/parser');
var diff = require('./diff');
var scope = require('./scope');

var fileUtils = require('./fileUtils');

// Plugins
var RTLPlugin = require('./plugin/rtl');
var ImportCollectorPlugin = require('./plugin/import-collector');
var VariableCollectorPlugin = require('./plugin/variable-collector');

// Workaround for a performance issue in the "css" parser module when used in combination
// with the "colors" module that enhances the String prototype.
// See: https://github.com/reworkcss/css/issues/88
//
// This function removes all properties added by "colors" and returns a function
// that restores them  afterwards.
// To be used before/after calling "css.parse"
function cleanupStringPrototype() {
	var customGetters = {}, customProps = {}, s = "", key;

	for (key in s) {
		if (!s.hasOwnProperty(key)) {
			var getter = String.prototype.__lookupGetter__(key);
			if (typeof getter === "function") {
				customGetters[key] = getter;
			} else {
				customProps[key] = String.prototype[key];
			}
			if (typeof Reflect !== "undefined") {
				Reflect.deleteProperty(String.prototype, key);
			} else {
				delete String.prototype[key];
			}
		}
	}

	return function restore() {
		for (key in customGetters) {
			if (customGetters.hasOwnProperty(key)) {
				String.prototype.__defineGetter__(key, customGetters[key]);
			}
		}
		for (key in customProps) {
			if (customProps.hasOwnProperty(key)) {
				String.prototype[key] = customProps[key];
			}
		}
	}
}

function dotThemingFileToPath(s) {
	if (s.indexOf(".") > -1) {
		s = "../" + s.replace(/\./g, "/");
	}

	return s;
}

var Builder = function() {
	this.themeCacheMapping = {};
};

Builder.prototype.getThemeCache = function(rootPath) {
	return this.themeCacheMapping[rootPath];
};

Builder.prototype.setThemeCache = function(rootPath, cache) {
	return this.themeCacheMapping[rootPath] = cache;
};

Builder.prototype.clearCache = function() {
	this.themeCacheMapping = {};
}

Builder.prototype.cacheTheme = function(result) {
	var that = this;
	var rootpath;

	// Theme can only be cached if list of imports is available
	if (result.imports.length === 0) {
		return Promise.resolve(result);
	}

	// Rootpath of theme is always the first entry
	rootpath = result.imports[0];

	return fileUtils.statFiles(result.imports).then(function(stats) {
		that.setThemeCache(rootpath, {
			result: result,
			stats: stats
		});

		return result;
	});

};

Builder.prototype.build = function(options) {
	var that = this;

	// Stores mapping between "virtual" paths (used within LESS) and real filesystem paths.
	// Only relevant when using the "rootPaths" option.
	var mFileMappings = {};

	// Assign default options
	options = assign({
		lessInput: null,
		lessInputPath: null,
		rtl: true,
		rootPaths: [],
		parser: {},
		compiler: {},
		library: {}
	}, options);

	// Set default of "relativeUrls" parser option to "true" (less default is "false")
	if (!options.parser.hasOwnProperty("relativeUrls")) {
		options.parser.relativeUrls = true;
	}

	function fileHandler(file, currentFileInfo, handleDataAndCallCallback, callback) {

		var pathname = path.normalize(currentFileInfo.currentDirectory + file);

		fileUtils.readFile(pathname, options.rootPaths).then(function(result) {
			if (!result) {
				console.log("File not found");
				callback({ type: 'File', message: "'" + file + "' wasn't found" })
			} else {

				try {
					// Save import mapping to calculate full import paths later on
					mFileMappings[currentFileInfo.rootFilename] = mFileMappings[currentFileInfo.rootFilename] || {};
					mFileMappings[currentFileInfo.rootFilename][pathname] = result.path;

					handleDataAndCallCallback(pathname, result.content);
				} catch (e) {
					console.log(e);
					callback(e);
				}

			}
		}, function(err) {
			console.log(err);
			callback(err);
		});
	}

	function compile(config) {
		return new Promise(function(resolve, reject) {

			var parserOptions = clone(options.parser);
			var rootpath;

			if (config.path) {
				// Keep rootpath for later usage in the ImportCollectorPlugin
				rootpath = config.path;
				parserOptions.filename = config.localPath;
			}

			// Keep filename for later usage (see ImportCollectorPlugin) as less modifies the parserOptions.filename
			var filename = parserOptions.filename;

			// Only use custom file handler when "rootPaths" are defined
			var fnFileHandler;
			if (options.rootPaths && options.rootPaths.length > 0) {
				fnFileHandler = fileHandler;
			}

			var parser = createParser(parserOptions, fnFileHandler);

			parser.parse(config.content, function(err, tree) {

				if (err) {
					reject(err);
					return;
				}

				var result = {};

				result.tree = tree;

				// plugins to collect imported files and variable values
				var oImportCollector = new ImportCollectorPlugin({
					importMappings: mFileMappings[filename]
				});
				var oVariableCollector = new VariableCollectorPlugin();

				// render to css
				result.css = tree.toCSS(assign(options.compiler, {
					plugins: [ oImportCollector, oVariableCollector ]
				}));

				// retrieve imported files
				result.imports = oImportCollector.getImports();

				// retrieve imported variables
				result.variables = oVariableCollector.getVariables();

				// also compile rtl-version if requested
				if (options.rtl) {
					result.cssRtl = tree.toCSS(assign(options.compiler, {
						plugins: [ new RTLPlugin() ]
					}));
				}

				if (rootpath) {
					result.imports.unshift(rootpath);
				}

				resolve(result);

			});
		});
	}

	function addInlineParameters(result) {
		return new Promise(function(resolve, reject) {
			if (typeof options.library === "object" && typeof options.library.name === "string") {
				var parameters = JSON.stringify(result.variables);

				// escape all chars that could cause problems with css parsers using URI-Encoding (% + HEX-Code)
				var escapedChars = "%{}()'\"\\";
				for (var i = 0; i < escapedChars.length; i++) {
					var char = escapedChars.charAt(i);
					var hex = char.charCodeAt(0).toString(16).toUpperCase();
					parameters = parameters.replace(new RegExp("\\" + char, "g"), "%" + hex);
				}

				var parameterStyleRule =
					"\n/* Inline theming parameters */\n#sap-ui-theme-" +
					options.library.name.replace(/\./g, "\\.") +
					" { background-image: url('data:text/plain;utf-8," + parameters + "'); }\n";

				// embed parameter variables as plain-text string into css
				result.css += parameterStyleRule;
				if (result.cssRtl) {
					result.cssRtl += parameterStyleRule;
				}
			}
			resolve(result);
		});
	}

	function readDotTheming(dotThemingInputPath) {
		return fileUtils.readFile(dotThemingInputPath, options.rootPaths).then(function(result) {

			var dotTheming;
			var dotThemingFilePath;

			if (result) {
				dotTheming = JSON.parse(result.content);
				dotThemingFilePath = result.path;
			}

			if (dotTheming && dotTheming.mCssScopes) {

				// Currently only the "library" target is supported
				var cssScope = dotTheming.mCssScopes["library"];

				if (cssScope) {

					var aScopes = cssScope.aScopes;
					var oScopeConfig = aScopes[0]; // Currenlty only one scope is supported

					var sBaseFile = dotThemingFileToPath(cssScope.sBaseFile || cssScopeTarget);
					var sEmbeddedCompareFile = dotThemingFileToPath(oScopeConfig.sEmbeddedCompareFile);
					var sEmbeddedFile = dotThemingFileToPath(oScopeConfig.sEmbeddedFile);

					// Currently, only support the use case when "sBaseFile" equals "sEmbeddedCompareFile"
					if (sBaseFile !== sEmbeddedCompareFile) {
						throw new Error("Unsupported content in \"" + dotThemingInputPath + "\": " +
							"\"sBaseFile\" (\"" + (cssScope.sBaseFile || cssScopeTarget) + "\") must be identical with " +
							"\"sEmbeddedCompareFile\" (\"" + oScopeConfig.sEmbeddedCompareFile + "\")");
					}

					var embeddedCompareFilePath = path.posix.join(themeDir, sEmbeddedCompareFile) + '.source.less';
					var embeddedFilePath = path.posix.join(themeDir, sEmbeddedFile) + '.source.less';

					// 1. Compile base + embedded files (both default + RTL variants)
					return Promise.all([
						fileUtils.readFile(embeddedCompareFilePath, options.rootPaths).then(compile),
						fileUtils.readFile(embeddedFilePath, options.rootPaths).then(compile)
					]).then(function(results) {
						return {
							embeddedCompare: results[0],
							embedded: results[1]
						};
					}).then(function(results) {

						// baseFile or embeddedFile
						var sLibraryBaseFile = dotTheming.mCssScopes.library.sBaseFile;
						var sScopeName = oScopeConfig.sSelector;

						function applyScope(embeddedCompareCss, embeddedCss, bRtl) {

							var restoreStringPrototype = cleanupStringPrototype();

							// Create diff object between embeddedCompare and embedded
							var oBase = css.parse(embeddedCompareCss);
							var oEmbedded = css.parse(embeddedCss);

							restoreStringPrototype();

							var oDiff = diff(oBase, oEmbedded);

							// Create scope
							var sScopeSelector = '.' + sScopeName;
							var oScope = scope(oDiff.diff, sScopeSelector);

							var oCssScopeRoot;

							if (oDiff.stack) {
								oCssScopeRoot = scope.scopeCssRoot(oDiff.stack.stylesheet.rules, sScopeName);

								if (oCssScopeRoot) {
									oScope.stylesheet.rules.unshift(oCssScopeRoot);
								}
							}

							// Append scope + stack to embeddedCompareFile (actually target file, which is currently always the same i.e. "library.css")
							// The stack gets appended to the embeddedFile only
							var sAppend = css.stringify(oScope);

							if (sLibraryBaseFile !== "library" && oDiff.stack && oDiff.stack.stylesheet.rules.length > 0) {
								sAppend +=  "\n" + css.stringify(oDiff.stack);
							}

							return sAppend + "\n";

						}

						results.embeddedCompare.css += applyScope(results.embeddedCompare.css, results.embedded.css);
						if (options.rtl) {
							results.embeddedCompare.cssRtl += applyScope(results.embeddedCompare.cssRtl, results.embedded.cssRtl, true);
						}

						// Create diff between embeddedCompare and embeded variables
						var oVariablesBase = results.embeddedCompare.variables;
						var oVariablesEmbedded = results.embedded.variables;
						var oVariablesDiff = {};

						for (var sKey in oVariablesEmbedded) {
							if (sKey in oVariablesBase) {
								if (oVariablesBase[sKey] != oVariablesEmbedded[sKey]) {
									oVariablesDiff[sKey] = oVariablesEmbedded[sKey];
								}
							}
						}

						// Merge variables
						var oVariables = {};
						oVariables["default"] = oVariablesBase;
						oVariables["scopes"] = {};
						oVariables["scopes"][sScopeName] = oVariablesDiff;

						results.embeddedCompare.variables = oVariables;

						var concatImports = function(aImportsBase, aImportsEmbedded) {
							var aConcats = aImportsBase.concat(aImportsEmbedded);

							return aConcats.filter(function(sImport, sIndex) {
								return aConcats.indexOf(sImport) == sIndex;
							})
						};

						if (sLibraryBaseFile !== "library") {
							results.embeddedCompare.imports = concatImports(results.embedded.imports, results.embeddedCompare.imports);
						} else {
							results.embeddedCompare.imports = concatImports(results.embeddedCompare.imports, results.embedded.imports);
						}

						// add .theming file to result.embeddedCompare.imports
						results.embeddedCompare.imports.push(dotThemingFilePath);

						// 6. Resolve promise with complete result object (css, cssRtl, variables, imports)
						return results.embeddedCompare;

					});
				}
			}

			// No css diffing and scoping
			return fileUtils.readFile(options.lessInputPath, options.rootPaths).then(compile);
		});
	}

	if (options.lessInput && options.lessInputPath) {
		return Promise.reject(new Error("Please only provide either `lessInput` or `lessInputPath` but not both."));
	}

	if (!options.lessInput && !options.lessInputPath) {
		return Promise.reject(new Error("Missing required option. Please provide either `lessInput` or `lessInputPath`."));
	}

	if (options.lessInput) {

		return compile({
			content: options.lessInput
		}).then(addInlineParameters).then(that.cacheTheme.bind(that));

	} else {
		var themeDir = path.posix.dirname(options.lessInputPath);

		// Always use the sap/ui/core library to lookup .theming files
		var coreThemeDir = themeDir.replace(/^.*\/themes\//, "/sap/ui/core/themes/");
		var dotThemingInputPath = path.posix.join(coreThemeDir, '.theming');

		return fileUtils.findFile(options.lessInputPath, options.rootPaths).then(function(fileInfo) {

			if (!fileInfo) {
				throw new Error("`lessInputPath` " + options.lessInputPath + " could not be found.");
			}

			// check theme has been already cached
			var themeCache;
			if (fileInfo.path) {
				themeCache = that.getThemeCache(fileInfo.path);
			}

			// Compile theme if not cached or RTL is requested and missing in cache
			if (!themeCache || (options.rtl && !themeCache.result.cssRtl)) {
				return readDotTheming(dotThemingInputPath).then(addInlineParameters).then(that.cacheTheme.bind(that));
			} else {
				return fileUtils.statFiles(themeCache.result.imports).then(function(newStats) {
					for (var i = 0; i < newStats.length; i++) {
						// check if .theming and less files have changed since last less compilation
						if (!newStats[i] || newStats[i].stat.mtime.getTime() !== themeCache.stats[i].stat.mtime.getTime()) {
							return readDotTheming(dotThemingInputPath).then(addInlineParameters).then(that.cacheTheme.bind(that));
						}
					}

					// serve from cache
					return themeCache.result;
				});
			}
		});
	}
};

module.exports.Builder = Builder;
<commit_msg>Extract scoping from loading dotthemefile<commit_after>// Copyright 2017 SAP SE.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http: //www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific
// language governing permissions and limitations under the License.

'use strict';

var path = require('path');
var assign = require('object-assign');
var clone = require('clone');
var css = require('css');

var createParser = require('./less/parser');
var diff = require('./diff');
var scope = require('./scope');

var fileUtils = require('./fileUtils');

// Plugins
var RTLPlugin = require('./plugin/rtl');
var ImportCollectorPlugin = require('./plugin/import-collector');
var VariableCollectorPlugin = require('./plugin/variable-collector');

// Workaround for a performance issue in the "css" parser module when used in combination
// with the "colors" module that enhances the String prototype.
// See: https://github.com/reworkcss/css/issues/88
//
// This function removes all properties added by "colors" and returns a function
// that restores them  afterwards.
// To be used before/after calling "css.parse"
function cleanupStringPrototype() {
	var customGetters = {}, customProps = {}, s = "", key;

	for (key in s) {
		if (!s.hasOwnProperty(key)) {
			var getter = String.prototype.__lookupGetter__(key);
			if (typeof getter === "function") {
				customGetters[key] = getter;
			} else {
				customProps[key] = String.prototype[key];
			}
			if (typeof Reflect !== "undefined") {
				Reflect.deleteProperty(String.prototype, key);
			} else {
				delete String.prototype[key];
			}
		}
	}

	return function restore() {
		for (key in customGetters) {
			if (customGetters.hasOwnProperty(key)) {
				String.prototype.__defineGetter__(key, customGetters[key]);
			}
		}
		for (key in customProps) {
			if (customProps.hasOwnProperty(key)) {
				String.prototype[key] = customProps[key];
			}
		}
	}
}

function dotThemingFileToPath(s) {
	if (s.indexOf(".") > -1) {
		s = "../" + s.replace(/\./g, "/");
	}

	return s;
}

var Builder = function() {
	this.themeCacheMapping = {};
};

Builder.prototype.getThemeCache = function(rootPath) {
	return this.themeCacheMapping[rootPath];
};

Builder.prototype.setThemeCache = function(rootPath, cache) {
	return this.themeCacheMapping[rootPath] = cache;
};

Builder.prototype.clearCache = function() {
	this.themeCacheMapping = {};
}

Builder.prototype.cacheTheme = function(result) {
	var that = this;
	var rootpath;

	// Theme can only be cached if list of imports is available
	if (result.imports.length === 0) {
		return Promise.resolve(result);
	}

	// Rootpath of theme is always the first entry
	rootpath = result.imports[0];

	return fileUtils.statFiles(result.imports).then(function(stats) {
		that.setThemeCache(rootpath, {
			result: result,
			stats: stats
		});

		return result;
	});

};

Builder.prototype.build = function(options) {
	var that = this;

	// Stores mapping between "virtual" paths (used within LESS) and real filesystem paths.
	// Only relevant when using the "rootPaths" option.
	var mFileMappings = {};

	// Assign default options
	options = assign({
		lessInput: null,
		lessInputPath: null,
		rtl: true,
		rootPaths: [],
		parser: {},
		compiler: {},
		library: {}
	}, options);

	// Set default of "relativeUrls" parser option to "true" (less default is "false")
	if (!options.parser.hasOwnProperty("relativeUrls")) {
		options.parser.relativeUrls = true;
	}

	function fileHandler(file, currentFileInfo, handleDataAndCallCallback, callback) {

		var pathname = path.normalize(currentFileInfo.currentDirectory + file);

		fileUtils.readFile(pathname, options.rootPaths).then(function(result) {
			if (!result) {
				console.log("File not found");
				callback({ type: 'File', message: "'" + file + "' wasn't found" })
			} else {

				try {
					// Save import mapping to calculate full import paths later on
					mFileMappings[currentFileInfo.rootFilename] = mFileMappings[currentFileInfo.rootFilename] || {};
					mFileMappings[currentFileInfo.rootFilename][pathname] = result.path;

					handleDataAndCallCallback(pathname, result.content);
				} catch (e) {
					console.log(e);
					callback(e);
				}

			}
		}, function(err) {
			console.log(err);
			callback(err);
		});
	}

	function compile(config) {
		return new Promise(function(resolve, reject) {

			var parserOptions = clone(options.parser);
			var rootpath;

			if (config.path) {
				// Keep rootpath for later usage in the ImportCollectorPlugin
				rootpath = config.path;
				parserOptions.filename = config.localPath;
			}

			// Keep filename for later usage (see ImportCollectorPlugin) as less modifies the parserOptions.filename
			var filename = parserOptions.filename;

			// Only use custom file handler when "rootPaths" are defined
			var fnFileHandler;
			if (options.rootPaths && options.rootPaths.length > 0) {
				fnFileHandler = fileHandler;
			}

			var parser = createParser(parserOptions, fnFileHandler);

			parser.parse(config.content, function(err, tree) {

				if (err) {
					reject(err);
					return;
				}

				var result = {};

				result.tree = tree;

				// plugins to collect imported files and variable values
				var oImportCollector = new ImportCollectorPlugin({
					importMappings: mFileMappings[filename]
				});
				var oVariableCollector = new VariableCollectorPlugin();

				// render to css
				result.css = tree.toCSS(assign(options.compiler, {
					plugins: [ oImportCollector, oVariableCollector ]
				}));

				// retrieve imported files
				result.imports = oImportCollector.getImports();

				// retrieve imported variables
				result.variables = oVariableCollector.getVariables();

				// also compile rtl-version if requested
				if (options.rtl) {
					result.cssRtl = tree.toCSS(assign(options.compiler, {
						plugins: [ new RTLPlugin() ]
					}));
				}

				if (rootpath) {
					result.imports.unshift(rootpath);
				}

				resolve(result);

			});
		});
	}

	function addInlineParameters(result) {
		return new Promise(function(resolve, reject) {
			if (typeof options.library === "object" && typeof options.library.name === "string") {
				var parameters = JSON.stringify(result.variables);

				// escape all chars that could cause problems with css parsers using URI-Encoding (% + HEX-Code)
				var escapedChars = "%{}()'\"\\";
				for (var i = 0; i < escapedChars.length; i++) {
					var char = escapedChars.charAt(i);
					var hex = char.charCodeAt(0).toString(16).toUpperCase();
					parameters = parameters.replace(new RegExp("\\" + char, "g"), "%" + hex);
				}

				var parameterStyleRule =
					"\n/* Inline theming parameters */\n#sap-ui-theme-" +
					options.library.name.replace(/\./g, "\\.") +
					" { background-image: url('data:text/plain;utf-8," + parameters + "'); }\n";

				// embed parameter variables as plain-text string into css
				result.css += parameterStyleRule;
				if (result.cssRtl) {
					result.cssRtl += parameterStyleRule;
				}
			}
			resolve(result);
		});
	}

	function compileWithScope(scopeOptions) {
		// 1. Compile base + embedded files (both default + RTL variants)
		return Promise.all([
			fileUtils.readFile(scopeOptions.embeddedCompareFilePath, options.rootPaths).then(compile),
			fileUtils.readFile(scopeOptions.embeddedFilePath, options.rootPaths).then(compile)
		]).then(function(results) {
			return {
				embeddedCompare: results[0],
				embedded: results[1]
			};
		}).then(function(results) {

			// baseFile or embeddedFile
			var sLibraryBaseFile = scopeOptions.baseFile;
			var sScopeName = scopeOptions.selector;

			function applyScope(embeddedCompareCss, embeddedCss, bRtl) {

				var restoreStringPrototype = cleanupStringPrototype();

				// Create diff object between embeddedCompare and embedded
				var oBase = css.parse(embeddedCompareCss);
				var oEmbedded = css.parse(embeddedCss);

				restoreStringPrototype();

				var oDiff = diff(oBase, oEmbedded);

				// Create scope
				var sScopeSelector = '.' + sScopeName;
				var oScope = scope(oDiff.diff, sScopeSelector);

				var oCssScopeRoot;

				if (oDiff.stack) {
					oCssScopeRoot = scope.scopeCssRoot(oDiff.stack.stylesheet.rules, sScopeName);

					if (oCssScopeRoot) {
						oScope.stylesheet.rules.unshift(oCssScopeRoot);
					}
				}

				// Append scope + stack to embeddedCompareFile (actually target file, which is currently always the same i.e. "library.css")
				// The stack gets appended to the embeddedFile only
				var sAppend = css.stringify(oScope);

				if (sLibraryBaseFile !== "library" && oDiff.stack && oDiff.stack.stylesheet.rules.length > 0) {
					sAppend += "\n" + css.stringify(oDiff.stack);
				}

				return sAppend + "\n";

			}

			results.embeddedCompare.css += applyScope(results.embeddedCompare.css, results.embedded.css);
			if (options.rtl) {
				results.embeddedCompare.cssRtl += applyScope(results.embeddedCompare.cssRtl, results.embedded.cssRtl, true);
			}

			// Create diff between embeddedCompare and embeded variables
			var oVariablesBase = results.embeddedCompare.variables;
			var oVariablesEmbedded = results.embedded.variables;
			var oVariablesDiff = {};

			for (var sKey in oVariablesEmbedded) {
				if (sKey in oVariablesBase) {
					if (oVariablesBase[sKey] != oVariablesEmbedded[sKey]) {
						oVariablesDiff[sKey] = oVariablesEmbedded[sKey];
					}
				}
			}

			// Merge variables
			var oVariables = {};
			oVariables["default"] = oVariablesBase;
			oVariables["scopes"] = {};
			oVariables["scopes"][sScopeName] = oVariablesDiff;

			results.embeddedCompare.variables = oVariables;

			var concatImports = function(aImportsBase, aImportsEmbedded) {
				var aConcats = aImportsBase.concat(aImportsEmbedded);

				return aConcats.filter(function(sImport, sIndex) {
					return aConcats.indexOf(sImport) == sIndex;
				})
			};

			if (sLibraryBaseFile !== "library") {
				results.embeddedCompare.imports = concatImports(results.embedded.imports, results.embeddedCompare.imports);
			} else {
				results.embeddedCompare.imports = concatImports(results.embeddedCompare.imports, results.embedded.imports);
			}

			// 6. Resolve promise with complete result object (css, cssRtl, variables, imports)
			return results.embeddedCompare;

		});
	}

	function readDotTheming(dotThemingInputPath) {
		return fileUtils.readFile(dotThemingInputPath, options.rootPaths).then(function(result) {

			var dotTheming;
			var dotThemingFilePath;

			if (result) {
				dotTheming = JSON.parse(result.content);
				dotThemingFilePath = result.path;
			}

			if (dotTheming && dotTheming.mCssScopes) {

				// Currently only the "library" target is supported
				var cssScope = dotTheming.mCssScopes["library"];

				if (cssScope) {

					var aScopes = cssScope.aScopes;
					var oScopeConfig = aScopes[0]; // Currenlty only one scope is supported

					var sBaseFile = dotThemingFileToPath(cssScope.sBaseFile || cssScopeTarget);
					var sEmbeddedCompareFile = dotThemingFileToPath(oScopeConfig.sEmbeddedCompareFile);
					var sEmbeddedFile = dotThemingFileToPath(oScopeConfig.sEmbeddedFile);

					// Currently, only support the use case when "sBaseFile" equals "sEmbeddedCompareFile"
					if (sBaseFile !== sEmbeddedCompareFile) {
						throw new Error("Unsupported content in \"" + dotThemingInputPath + "\": " +
							"\"sBaseFile\" (\"" + (cssScope.sBaseFile || cssScopeTarget) + "\") must be identical with " +
							"\"sEmbeddedCompareFile\" (\"" + oScopeConfig.sEmbeddedCompareFile + "\")");
					}

					var embeddedCompareFilePath = path.posix.join(themeDir, sEmbeddedCompareFile) + '.source.less';
					var embeddedFilePath = path.posix.join(themeDir, sEmbeddedFile) + '.source.less';

					// 1. Compile base + embedded files (both default + RTL variants)
					return compileWithScope({
						selector: oScopeConfig.sSelector,
						embeddedFilePath: embeddedFilePath,
						embeddedCompareFilePath: embeddedCompareFilePath,
						baseFile: dotTheming.mCssScopes.library.sBaseFile
					}).then(function(embeddedCompare) {
						embeddedCompare.imports.push(dotThemingFilePath);
						return embeddedCompare;
					});
				}
			}

			// No css diffing and scoping
			return fileUtils.readFile(options.lessInputPath, options.rootPaths).then(compile);
		});
	}

	if (options.lessInput && options.lessInputPath) {
		return Promise.reject(new Error("Please only provide either `lessInput` or `lessInputPath` but not both."));
	}

	if (!options.lessInput && !options.lessInputPath) {
		return Promise.reject(new Error("Missing required option. Please provide either `lessInput` or `lessInputPath`."));
	}

	if (options.lessInput) {

		return compile({
			content: options.lessInput
		}).then(addInlineParameters).then(that.cacheTheme.bind(that));

	} else {
		var themeDir = path.posix.dirname(options.lessInputPath);

		// Always use the sap/ui/core library to lookup .theming files
		var coreThemeDir = themeDir.replace(/^.*\/themes\//, "/sap/ui/core/themes/");
		var dotThemingInputPath = path.posix.join(coreThemeDir, '.theming');

		return fileUtils.findFile(options.lessInputPath, options.rootPaths).then(function(fileInfo) {

			if (!fileInfo) {
				throw new Error("`lessInputPath` " + options.lessInputPath + " could not be found.");
			}

			// check theme has been already cached
			var themeCache;
			if (fileInfo.path) {
				themeCache = that.getThemeCache(fileInfo.path);
			}

			// Compile theme if not cached or RTL is requested and missing in cache
			if (!themeCache || (options.rtl && !themeCache.result.cssRtl)) {
				return readDotTheming(dotThemingInputPath).then(addInlineParameters).then(that.cacheTheme.bind(that));
			} else {
				return fileUtils.statFiles(themeCache.result.imports).then(function(newStats) {
					for (var i = 0; i < newStats.length; i++) {
						// check if .theming and less files have changed since last less compilation
						if (!newStats[i] || newStats[i].stat.mtime.getTime() !== themeCache.stats[i].stat.mtime.getTime()) {
							return readDotTheming(dotThemingInputPath).then(addInlineParameters).then(that.cacheTheme.bind(that));
						}
					}

					// serve from cache
					return themeCache.result;
				});
			}
		});
	}
};

module.exports.Builder = Builder;
<|endoftext|>


<commit_before>/**
 * CG Space Invaders
 * CG45179 16'17
 *
 * @author: Rui Ventura ( ist181045 )
 * @author: Diogo Freitas ( ist181586 )
 * @author: Sara Azinhal ( ist181700 )
 */

class AlliedShip extends THREE.Object3D {

	constructor ( x, y, z ) {

		super();

		this.max_speed = 750;
		this.acceleration = 100;
		this.friction = 37.5;
		this.direction = "none";
		this.velocity = 0;

		this.addASCenter( this, 0, 0, 0 );
		this.addASPropulsor( this, 0, 0, 2.5 );
		this.addASCanon( this, 0, 0, -7.5 );
		this.addASGun( this, 2, 0, -12 );
		this.addASGun( this, -2, 0, -12 );
		this.addASGunCockpit1( this, -2.5, -1.25, -7.5 );
		this.addASGunCockpit2( this, 2.5, -1.25, -7.5 );
		//this.addASLeg( this, 0, -9, 0 );

		this.boundingBox = new THREE.Box3();
		this.boundingBox.setFromObject(this);
		this.boundingSphere = new THREE.Sphere();
		this.boundingBox.getBoundingSphere(this.boundingSphere);

		this.position.set( x, y, z );
		//a
	}

	getObj () {
		return this;
	}

	addASCenter ( obj, x, y, z ) {
		var geometry = new THREE.CylinderGeometry( 10, 10, 2.5, 50, 2.5, false );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASPropulsor ( obj, x, y, z ) {
		var geometry = new THREE.CylinderGeometry( 8.75, 8.75, 1.75, 50, 1.75, false );
		var mesh = new THREE.Mesh( geometry, material2 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASCanon ( obj, x, y, z ) {
		var geometry = new THREE.CubeGeometry( 2.5, 2.5, 8.75 );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASGun ( obj, x, y, z ) {
		var geometry = new THREE.CubeGeometry( 1, 2.5, 9 );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASGunCockpit1 ( obj, x, y, z ) {
		var triangleShape = new THREE.Shape();
		triangleShape.moveTo( 0, 0 );
		triangleShape.lineTo( 0, 4 );
		triangleShape.lineTo( 9, 0 );
		triangleShape.lineTo( 0, 0 );

		var geometry = new THREE.ExtrudeGeometry( triangleShape, { amount: 2.5, bevelEnabled: false } );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.rotation.x = -Math.PI/2;
		mesh.rotation.z = Math.PI/2;
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASGunCockpit2 ( obj, x, y, z ) {
		var triangleShape = new THREE.Shape();
		triangleShape.moveTo( 0, 0 );
		triangleShape.lineTo( 0, 4 );
		triangleShape.lineTo( 9, 0 );
		triangleShape.lineTo( 0, 0 );

		var geometry = new THREE.ExtrudeGeometry( triangleShape, { amount: -2.5, bevelEnabled: false } );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.rotation.x = Math.PI/2;
		mesh.rotation.z = -Math.PI/2;
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASLeg ( obj, x, y, z ) {
		var geometry = new THREE.CubeGeometry( 2, 0.5, 2 );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, (y - 5), z );

		var geometry1 = new THREE.CylinderGeometry( 1, 1, 2, 20, 2, false );
		var mesh1 = new THREE.Mesh( geometry1, material1 );
		mesh1.position.set( x, y, z );

		obj.add( mesh );
		obj.add( mesh1 );
	}

	updateBoundingBox() {
		this.boundingBox.setFromObject(this);
		this.boundingBox.getBoundingSphere(this.boundingSphere);
	}

	move ( interval ) {
		if ( this.direction == "left" ) {
			this.velocity -= ( this.acceleration - this.friction ) * interval;
		}

		if ( this.direction == "right" ) {
			this.velocity += ( this.acceleration - this.friction )  * interval;
		}

		if ( this.direction == "none" ) {
			if ( this.velocity < 0 ) {
				this.velocity += ( this.friction * 2 ) * interval;
				if ( this.velocity > 0 ) {
					this.velocity = 0;
				}
			}

			if ( this.velocity > 0 ) {
				this.velocity -= ( this.friction * 2 ) * interval;
				if ( this.velocity < 0 ) {
					this.velocity = 0;
				}
			}
		}

		this.position.x += this.velocity * interval;
		this.updateBoundingBox();

		this.wallCollision();

		cameraDynamic.position.x += this.velocity * interval;
	}

	fire () {
		if (WEAPONS_SYSTEM == 1){
			var b = new Bullet(this.position.x, 10, this.position.z - 14.5);
			scene.add(b);
			GameField.Bullets.push(b);
		}

		if (WEAPONS_SYSTEM == 2){
			var b1 = new Bullet(this.position.x - 2, 10, this.position.z - 18);
			var b2 = new Bullet(this.position.x + 2, 10, this.position.z - 18);
			scene.add(b1);
			scene.add(b2);
			GameField.Bullets.push(b1);
			GameField.Bullets.push(b2);
		}
	}

	wallCollision () {
		if ( this.boundingBox.intersectsBox(GameField.rbbb) ) {
			this.position.x = GameField.rbbb.min.x - 10;
			this.velocity = 0;
			this.direction = "none";
		}

		if ( this.boundingBox.intersectsBox(GameField.lbbb) ) {
			this.position.x = GameField.lbbb.max.x + 10;
			this.velocity = 0;
			this.direction = "none";
		}
	}
}
<commit_msg>Commit check<commit_after>/**
 * CG Space Invaders
 * CG45179 16'17
 *
 * @author: Rui Ventura ( ist181045 )
 * @author: Diogo Freitas ( ist181586 )
 * @author: Sara Azinhal ( ist181700 )
 */

class AlliedShip extends THREE.Object3D {

	constructor ( x, y, z ) {

		super();

		this.max_speed = 750;
		this.acceleration = 100;
		this.friction = 37.5;
		this.direction = "none";
		this.velocity = 0;

		this.addASCenter( this, 0, 0, 0 );
		this.addASPropulsor( this, 0, 0, 2.5 );
		this.addASCanon( this, 0, 0, -7.5 );
		this.addASGun( this, 2, 0, -12 );
		this.addASGun( this, -2, 0, -12 );
		this.addASGunCockpit1( this, -2.5, -1.25, -7.5 );
		this.addASGunCockpit2( this, 2.5, -1.25, -7.5 );
		//this.addASLeg( this, 0, -9, 0 );

		this.boundingBox = new THREE.Box3();
		this.boundingBox.setFromObject(this);
		this.boundingSphere = new THREE.Sphere();
		this.boundingBox.getBoundingSphere(this.boundingSphere);

		this.position.set( x, y, z );
	}

	getObj () {
		return this;
	}

	addASCenter ( obj, x, y, z ) {
		var geometry = new THREE.CylinderGeometry( 10, 10, 2.5, 50, 2.5, false );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASPropulsor ( obj, x, y, z ) {
		var geometry = new THREE.CylinderGeometry( 8.75, 8.75, 1.75, 50, 1.75, false );
		var mesh = new THREE.Mesh( geometry, material2 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASCanon ( obj, x, y, z ) {
		var geometry = new THREE.CubeGeometry( 2.5, 2.5, 8.75 );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASGun ( obj, x, y, z ) {
		var geometry = new THREE.CubeGeometry( 1, 2.5, 9 );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASGunCockpit1 ( obj, x, y, z ) {
		var triangleShape = new THREE.Shape();
		triangleShape.moveTo( 0, 0 );
		triangleShape.lineTo( 0, 4 );
		triangleShape.lineTo( 9, 0 );
		triangleShape.lineTo( 0, 0 );

		var geometry = new THREE.ExtrudeGeometry( triangleShape, { amount: 2.5, bevelEnabled: false } );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.rotation.x = -Math.PI/2;
		mesh.rotation.z = Math.PI/2;
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASGunCockpit2 ( obj, x, y, z ) {
		var triangleShape = new THREE.Shape();
		triangleShape.moveTo( 0, 0 );
		triangleShape.lineTo( 0, 4 );
		triangleShape.lineTo( 9, 0 );
		triangleShape.lineTo( 0, 0 );

		var geometry = new THREE.ExtrudeGeometry( triangleShape, { amount: -2.5, bevelEnabled: false } );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.rotation.x = Math.PI/2;
		mesh.rotation.z = -Math.PI/2;
		mesh.position.set( x, y, z );

		obj.add( mesh );
	}

	addASLeg ( obj, x, y, z ) {
		var geometry = new THREE.CubeGeometry( 2, 0.5, 2 );
		var mesh = new THREE.Mesh( geometry, material1 );
		mesh.position.set( x, (y - 5), z );

		var geometry1 = new THREE.CylinderGeometry( 1, 1, 2, 20, 2, false );
		var mesh1 = new THREE.Mesh( geometry1, material1 );
		mesh1.position.set( x, y, z );

		obj.add( mesh );
		obj.add( mesh1 );
	}

	updateBoundingBox() {
		this.boundingBox.setFromObject(this);
		this.boundingBox.getBoundingSphere(this.boundingSphere);
	}

	move ( interval ) {
		if ( this.direction == "left" ) {
			this.velocity -= ( this.acceleration - this.friction ) * interval;
		}

		if ( this.direction == "right" ) {
			this.velocity += ( this.acceleration - this.friction )  * interval;
		}

		if ( this.direction == "none" ) {
			if ( this.velocity < 0 ) {
				this.velocity += ( this.friction * 2 ) * interval;
				if ( this.velocity > 0 ) {
					this.velocity = 0;
				}
			}

			if ( this.velocity > 0 ) {
				this.velocity -= ( this.friction * 2 ) * interval;
				if ( this.velocity < 0 ) {
					this.velocity = 0;
				}
			}
		}

		this.position.x += this.velocity * interval;
		this.updateBoundingBox();

		this.wallCollision();

		cameraDynamic.position.x += this.velocity * interval;
	}

	fire () {
		if (WEAPONS_SYSTEM == 1){
			var b = new Bullet(this.position.x, 10, this.position.z - 14.5);
			scene.add(b);
			GameField.Bullets.push(b);
		}

		if (WEAPONS_SYSTEM == 2){
			var b1 = new Bullet(this.position.x - 2, 10, this.position.z - 18);
			var b2 = new Bullet(this.position.x + 2, 10, this.position.z - 18);
			scene.add(b1);
			scene.add(b2);
			GameField.Bullets.push(b1);
			GameField.Bullets.push(b2);
		}
	}

	wallCollision () {
		if ( this.boundingBox.intersectsBox(GameField.rbbb) ) {
			this.position.x = GameField.rbbb.min.x - 10;
			this.velocity = 0;
			this.direction = "none";
		}

		if ( this.boundingBox.intersectsBox(GameField.lbbb) ) {
			this.position.x = GameField.lbbb.max.x + 10;
			this.velocity = 0;
			this.direction = "none";
		}
	}
}
<|endoftext|>


<commit_before>"use strict";

const gitUp = require("git-up");

/**
 * gitUrlParse
 * Parses a Git url.
 *
 * @name gitUrlParse
 * @function
 * @param {String} url The Git url to parse.
 * @return {GitUrl} The `GitUrl` object containing:
 *
 *  - `protocols` (Array): An array with the url protocols (usually it has one element).
 *  - `port` (null|Number): The domain port.
 *  - `resource` (String): The url domain (including subdomains).
 *  - `user` (String): The authentication user (usually for ssh urls).
 *  - `pathname` (String): The url pathname.
 *  - `hash` (String): The url hash.
 *  - `search` (String): The url querystring value.
 *  - `href` (String): The input url.
 *  - `protocol` (String): The git url protocol.
 *  - `token` (String): The oauth token (could appear in the https urls).
 *  - `source` (String): The Git provider (e.g. `"github.com"`).
 *  - `owner` (String): The repository owner.
 *  - `name` (String): The repository name.
 *  - `ref` (String): The repository ref (e.g., "master" or "dev").
 *  - `filepath` (String): A filepath relative to the repository root.
 *  - `filepathtype` (String): The type of filepath in the url ("blob" or "tree").
 *  - `full_name` (String): The owner and name values in the `owner/name` format.
 *  - `toString` (Function): A function to stringify the parsed url into another url type.
 *  - `organization` (String): The organization the owner belongs to. This is CloudForge specific.
 *
 */
function gitUrlParse(url) {

    if (typeof url !== "string") {
        throw new Error("The url must be a string.");
    }

    let urlInfo = gitUp(url)
      , sourceParts = urlInfo.resource.split(".")
      , splits = null
      ;

    urlInfo.toString = function (type) {
        return gitUrlParse.stringify(this, type);
    };

    urlInfo.source = sourceParts.length > 2
                   ? sourceParts.slice(-2).join(".")
                   : urlInfo.source = urlInfo.resource
                   ;

    urlInfo.name = urlInfo.pathname.substring(1).replace(/\.git$/, "");
    urlInfo.owner = urlInfo.user;
    urlInfo.organization = urlInfo.owner;

    switch (urlInfo.source) {
        case "cloudforge.com":
            urlInfo.owner = urlInfo.user;
            urlInfo.organization = sourceParts[0];
            break;
        default:
            splits = urlInfo.name.split("/");
            if (splits.length >= 2) {
                urlInfo.owner = splits[0];
                urlInfo.name = splits[1];
            }

            urlInfo.ref = "";
            urlInfo.filepathtype = "";
            urlInfo.filepath = "";
            if ((splits.length > 3) && (["blob", "tree"].indexOf(splits[2]) >= 0)) {
                urlInfo.filepathtype = splits[2];
                urlInfo.ref = splits[3];
                if (splits.length > 4) {
                    urlInfo.filepath = splits.slice(4).join('/');
                }
            }
            break;
    }

    urlInfo.full_name = urlInfo.owner;
    if (urlInfo.name) {
        urlInfo.full_name && (urlInfo.full_name += "/");
        urlInfo.full_name += urlInfo.name;
    }

    return urlInfo;
}

/**
 * stringify
 * Stringifies a `GitUrl` object.
 *
 * @name stringify
 * @function
 * @param {GitUrl} obj The parsed Git url object.
 * @param {String} type The type of the stringified url (default `obj.protocol`).
 * @return {String} The stringified url.
 */
gitUrlParse.stringify = function (obj, type) {
    type = type || ((obj.protocols && obj.protocols.length) ? obj.protocols.join('+') : obj.protocol);
    const port = obj.port ? `:${obj.port}` : '';
    const user = obj.user || 'git';
    switch (type) {
        case "ssh":
            if (port)
                return `ssh://${user}@${obj.resource}${port}/${obj.full_name}.git`;
            else
                return `${user}@${obj.resource}:${obj.full_name}.git`;
        case "git+ssh":
        case "ssh+git":
        case "ftp":
        case "ftps":
            return `${type}://${user}@${obj.resource}${port}/${obj.full_name}.git`;
        case "http":
        case "https":
            let token = "";
            if (obj.token) {
              token = buildToken(obj);
            }
            return `${type}://${token}${obj.resource}${port}/${obj.full_name}.git`;
        default:
            return obj.href;
    }
};

/*!
 * buildToken
 * Builds OAuth token prefix (helper function)
 *
 * @name buildToken
 * @function
 * @param {GitUrl} obj The parsed Git url object.
 * @return {String} token prefix
 */
function buildToken(obj) {
    switch (obj.source) {
      case "bitbucket.org":
        return `x-token-auth:${obj.token}@`;
      default:
        return `${obj.token}@`
    }
}

module.exports = gitUrlParse;
<commit_msg>Add support for Visual Studio Team Services URLs<commit_after>"use strict";

const gitUp = require("git-up");

/**
 * gitUrlParse
 * Parses a Git url.
 *
 * @name gitUrlParse
 * @function
 * @param {String} url The Git url to parse.
 * @return {GitUrl} The `GitUrl` object containing:
 *
 *  - `protocols` (Array): An array with the url protocols (usually it has one element).
 *  - `port` (null|Number): The domain port.
 *  - `resource` (String): The url domain (including subdomains).
 *  - `user` (String): The authentication user (usually for ssh urls).
 *  - `pathname` (String): The url pathname.
 *  - `hash` (String): The url hash.
 *  - `search` (String): The url querystring value.
 *  - `href` (String): The input url.
 *  - `protocol` (String): The git url protocol.
 *  - `token` (String): The oauth token (could appear in the https urls).
 *  - `source` (String): The Git provider (e.g. `"github.com"`).
 *  - `owner` (String): The repository owner.
 *  - `name` (String): The repository name.
 *  - `ref` (String): The repository ref (e.g., "master" or "dev").
 *  - `filepath` (String): A filepath relative to the repository root.
 *  - `filepathtype` (String): The type of filepath in the url ("blob" or "tree").
 *  - `full_name` (String): The owner and name values in the `owner/name` format.
 *  - `toString` (Function): A function to stringify the parsed url into another url type.
 *  - `organization` (String): The organization the owner belongs to. This is CloudForge specific.
 *
 */
function gitUrlParse(url) {

    if (typeof url !== "string") {
        throw new Error("The url must be a string.");
    }

    let urlInfo = gitUp(url)
      , sourceParts = urlInfo.resource.split(".")
      , splits = null
      ;

    urlInfo.toString = function (type) {
        return gitUrlParse.stringify(this, type);
    };

    urlInfo.source = sourceParts.length > 2
                   ? sourceParts.slice(-2).join(".")
                   : urlInfo.source = urlInfo.resource
                   ;

    urlInfo.name = urlInfo.pathname.substring(1).replace(/\.git$/, "");
    urlInfo.owner = urlInfo.user;
    urlInfo.organization = urlInfo.owner;

    switch (urlInfo.source) {
        case "cloudforge.com":
            urlInfo.owner = urlInfo.user;
            urlInfo.organization = sourceParts[0];
            break;
        case "visualstudio.com":
            splits = urlInfo.name.split("/");
            if (splits.length === 2) {
                urlInfo.owner = splits[1];
                urlInfo.name = splits[1];
            } else if (splits.length > 2) {
                urlInfo.owner = splits[0];
                urlInfo.name = splits[2];
            }
            break;
        default:
            splits = urlInfo.name.split("/");
            if (splits.length >= 2) {
                urlInfo.owner = splits[0];
                urlInfo.name = splits[1];
            }

            urlInfo.ref = "";
            urlInfo.filepathtype = "";
            urlInfo.filepath = "";
            if ((splits.length > 3) && (["blob", "tree"].indexOf(splits[2]) >= 0)) {
                urlInfo.filepathtype = splits[2];
                urlInfo.ref = splits[3];
                if (splits.length > 4) {
                    urlInfo.filepath = splits.slice(4).join('/');
                }
            }
            break;
    }

    urlInfo.full_name = urlInfo.owner;
    if (urlInfo.name) {
        urlInfo.full_name && (urlInfo.full_name += "/");
        urlInfo.full_name += urlInfo.name;
    }

    return urlInfo;
}

/**
 * stringify
 * Stringifies a `GitUrl` object.
 *
 * @name stringify
 * @function
 * @param {GitUrl} obj The parsed Git url object.
 * @param {String} type The type of the stringified url (default `obj.protocol`).
 * @return {String} The stringified url.
 */
gitUrlParse.stringify = function (obj, type) {
    type = type || ((obj.protocols && obj.protocols.length) ? obj.protocols.join('+') : obj.protocol);
    const port = obj.port ? `:${obj.port}` : '';
    const user = obj.user || 'git';
    switch (type) {
        case "ssh":
            if (port)
                return `ssh://${user}@${obj.resource}${port}/${obj.full_name}.git`;
            else
                return `${user}@${obj.resource}:${obj.full_name}.git`;
        case "git+ssh":
        case "ssh+git":
        case "ftp":
        case "ftps":
            return `${type}://${user}@${obj.resource}${port}/${obj.full_name}.git`;
        case "http":
        case "https":
            let token = "";
            if (obj.token) {
              token = buildToken(obj);
            }
            return `${type}://${token}${obj.resource}${port}/${obj.full_name}.git`;
        default:
            return obj.href;
    }
};

/*!
 * buildToken
 * Builds OAuth token prefix (helper function)
 *
 * @name buildToken
 * @function
 * @param {GitUrl} obj The parsed Git url object.
 * @return {String} token prefix
 */
function buildToken(obj) {
    switch (obj.source) {
      case "bitbucket.org":
        return `x-token-auth:${obj.token}@`;
      default:
        return `${obj.token}@`
    }
}

module.exports = gitUrlParse;
<|endoftext|>


<commit_before>/*
 * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
 *
 * @author Julius Härtl <jus@bitgrid.net>
 *
 * @license GNU AGPL version 3 or any later version
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/* global app oc_requesttoken markdownitLinkTarget */

import app from './App.js';
import md from 'angular-markdown-it';
import markdownitLinkTarget from 'markdown-it-link-target';
import markdownitCheckbox from 'legacy/markdown-it-checkbox.js';

app.config(function ($provide, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider, markdownItConverterProvider) {
	'use strict';
	$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;

	$compileProvider.debugInfoEnabled(true);

	markdownItConverterProvider.use(markdownitLinkTarget, {
		breaks: true,
		linkify: true,
		xhtmlOut: true
	}).use(markdownitCheckbox);

	$urlRouterProvider.otherwise('/');

	$stateProvider
		.state('list', {
			url: '/:filter',
			templateUrl: '/boardlist.mainView.html',
			controller: 'ListController',
			reloadOnSearch: false,
			params: {
				filter: {value: '', dynamic: true}
			}
		})
		.state('board', {
			url: '/board/:boardId/:filter',
			templateUrl: '/board.html',
			controller: 'BoardController',
			params: {
				filter: {value: '', dynamic: true}
			}
		})
		.state('board.detail', {
			url: '/detail/',
			reloadOnSearch: false,
			params: {
				tab: {value: 0, dynamic: true},
			},
			views: {
				'sidebarView': {
					templateUrl: '/board.sidebarView.html'
				}
			}
		})
		.state('board.card', {
			url: '/card/:cardId',
			params: {
				tab: {value: 0, dynamic: true},
			},
			views: {
				'sidebarView': {
					templateUrl: '/card.sidebarView.html',
					controller: 'CardController'
				}
			}
		});

	$provide.decorator('nvFileOverDirective', function ($delegate) {
		var directive = $delegate[0],
			link = directive.link;

		directive.compile = function () {
			return function (scope, element, attrs) {
				var overClass = attrs.overClass || 'nv-file-over';
				link.apply(this, arguments);
				let counter = 0;
				element.on('dragenter', function (event) {
					counter++;
				});
				element.on('dragleave', function (event) {
					counter--;
					if (counter <= 0) {
						$('.' + overClass).removeClass(overClass);
					}
				});
			};
		};

		return $delegate;
	});

});
<commit_msg>Fix markdownit configuration<commit_after>/*
 * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
 *
 * @author Julius Härtl <jus@bitgrid.net>
 *
 * @license GNU AGPL version 3 or any later version
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/* global app oc_requesttoken markdownitLinkTarget */

import app from './App.js';
import md from 'angular-markdown-it';
import markdownitLinkTarget from 'markdown-it-link-target';
import markdownitCheckbox from 'legacy/markdown-it-checkbox.js';

app.config(function ($provide, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider, markdownItConverterProvider) {
	'use strict';
	$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;

	$compileProvider.debugInfoEnabled(true);

	markdownItConverterProvider.config({
		breaks: true,
		linkify: true,
		xhtmlOut: true
	});
	markdownItConverterProvider.use(markdownitLinkTarget).use(markdownitCheckbox);

	$urlRouterProvider.otherwise('/');

	$stateProvider
		.state('list', {
			url: '/:filter',
			templateUrl: '/boardlist.mainView.html',
			controller: 'ListController',
			reloadOnSearch: false,
			params: {
				filter: {value: '', dynamic: true}
			}
		})
		.state('board', {
			url: '/board/:boardId/:filter',
			templateUrl: '/board.html',
			controller: 'BoardController',
			params: {
				filter: {value: '', dynamic: true}
			}
		})
		.state('board.detail', {
			url: '/detail/',
			reloadOnSearch: false,
			params: {
				tab: {value: 0, dynamic: true},
			},
			views: {
				'sidebarView': {
					templateUrl: '/board.sidebarView.html'
				}
			}
		})
		.state('board.card', {
			url: '/card/:cardId',
			params: {
				tab: {value: 0, dynamic: true},
			},
			views: {
				'sidebarView': {
					templateUrl: '/card.sidebarView.html',
					controller: 'CardController'
				}
			}
		});

	$provide.decorator('nvFileOverDirective', function ($delegate) {
		var directive = $delegate[0],
			link = directive.link;

		directive.compile = function () {
			return function (scope, element, attrs) {
				var overClass = attrs.overClass || 'nv-file-over';
				link.apply(this, arguments);
				let counter = 0;
				element.on('dragenter', function (event) {
					counter++;
				});
				element.on('dragleave', function (event) {
					counter--;
					if (counter <= 0) {
						$('.' + overClass).removeClass(overClass);
					}
				});
			};
		};

		return $delegate;
	});

});
<|endoftext|>