<commit_before>var figures = Npm.require('figures')

T = { // eslint-disable-line
  analize: function () { // this lets us analyze suite's structure to act accordingly later (allowing, for example, iit blocks to work)
    T.analyzing = true
    T.suites.forEach(exec)
    T.analyzing = false
  },
  run: function () {
    if (!process.env.RUN_TESTS) { return } // only run tests when explicitly told so

    var testingDB = new global.MongoInternals.RemoteCollectionDriver(T.testingDbUrl) // create a driver pointing to testing's DB
    getCollections().forEach(pointToTestingDB) // point collections to testing's DB
    getCollections().forEach(removeAll) // erase date on testing DB (though there should be none)

    // Run specs
    T.onlyRootDescribeBlocksForIit.length ? T.onlyRootDescribeBlocksForIit.forEach(exec) : T.suites.forEach(exec) // if there's `iit` blocks, only run those

    // output number of successful over total tests
    log('\n' + (T.itCount + ' tests: ').yellow + (T.successfulItCount + ' passing, ').green + (T.itCount - T.successfulItCount + ' failing.').red)

    getCollections().forEach(pointBackToDevelopDB) // point collections back to development's DB
    _(T.postRunCallbacks).reject(preventsSuiteFromRunning).map(fns).forEach(exec) // allow to run a callback when testing has finished (before possibly ending the process)

    process.env.RUN_TESTS !== 'cont' && process.exit(T.exceptions.length) // end the process unless option is specified

    function pointToTestingDB (collection) {
      collection.latte_original_driver = collection._driver // keep track of original driver, to point back to development's DB once tests have finished
      collection._driver = testingDB
      collection._driver.open(collection._name, collection._connection)
    }

    function pointBackToDevelopDB (collection) {
      collection.latte_original_driver.open(collection._name, collection._connection)
    }
  },
  describe: descriptionBlock('describe'),
  context: descriptionBlock('context'),
  ddescribe: descriptionBlock('describe', { runOnly: true }),
  ccontext: descriptionBlock('context', { runOnly: true }),
  iit: function (label, fn, options) {
    if (T.analyzing) {
      if (!_(T.onlyRootDescribeBlocksForIit).contains(T.currentRootDescribeBlock)) { // only if we have not already added the root describe
        // keeping track of the root describe allows us to run all before/after hooks from the beginning.
        // This also allows to print all labels from the beginning
        T.onlyRootDescribeBlocksForIit.push(T.currentRootDescribeBlock)
      }
    }
    options = options || {}
    T.it(label, fn, _(options).extend({ runOnly: true }))
  },
  it: function (label, fn, options) {
    if (T.analyzing) { return } // don't run `it` blocks when analyzing
    options = options || {}
    var msg = T.message('it', label, T.deepLevel) // generate msg for reports

    if (T.onlyRootDescribeBlocksForIit.length) { // if there's any `iit` block
      if (!options.runOnly) { return } // only run `iit` blocks
      msg = msg.underline
    }

    while (T.describeMessages.length) { // only print `describe` labels if their nested `it` blocks get executed
      console.log(T.describeMessages.shift())
    }

    T.itCount++ // count number of tests, for reports
    try {
      T.itBlockRunLevel = T.deepLevel
      T.beforeEachBlocks.map(fns).forEach(exec) // run beforeEach blocks
      fn()                                      // run assertions in it block
      T.afterEachBlocks.map(fns).forEach(exec)  // run afterEach blocks
      T.successfulItCount++                     // count number of successful tests, for reports
      log((msg + ' ' + figures.tick.green))                 // log into stdout tests label and result
    } catch (e) {
      if (e.message && e.message === "It\'s ok  to display this error, it\'s part of a test -12344321-") { // testing purposes
        T.successfulItCount++                     // count number of successful tests, for reports
      } else { // normal flow
        log(msg + ' ' + figures.cross.red)
        log(e.stack || e)
        T.exceptions.push(e)                      // if `T.exceptions` has any item at the en of the test run, exit code will be != 0
      }
    } finally {
      getCollections().forEach(removeAll)
    }
  },
  message: function (type, label, deepLevel) { // pretty print messages for console report
    var prefix = ''
    if (T.deepLevel === 0) { prefix += '~~~~~~~~'.grey + '\n' }
    prefix += _.range(deepLevel).reduce(addIndentation, '')
    return prefix + type.magenta.bold + ' ' + label.cyan

    function addIndentation (space) {
      return space + '  '
    }
  },
  ignore: function () {},
  suites: [],
  postRunCallbacks: [],
  describeBlocks: [],
  exceptions: [],
  deepLevel: 0,
  horizontalLevel: 0,
  itCount: 0,
  successfulItCount: 0,
  onlyRootDescribeBlocksForIit: [],
  describeMessages: [],
  itBlockRunLevel: 0,
  isFirstAddedSuite: true,
  analyzing: true,
  onlySuites: [],
  _contextBlocks: ['beforeEach', 'afterEach'],
  testingDbUrl: 'mongodb://127.0.0.1:3001/meteor_latte'
}

addContextBlocks() // adds before/after each/all functions

if (process.env.LATTE_SUITES) {
  T.onlySuites = T.onlySuites.concat(JSON.parse(process.env.LATTE_SUITES))
  if (T.onlySuites.length) {
    T.onlySuitesAsUserParams = true // if user specifies some suites to run, only run those
  }
}

function fns (obj) { return obj.fn }

function exec (fn) { fn() }

function removeAll (collection) { collection.remove({}) }

function descriptionBlock (type, options) {
  if (!process.env.RUN_TESTS) { return function () {} } // don't run any test related stuff unless explicitly told so
  options = options || {}

  return function analyzeOrExec (label, fn) {
    if (T.deepLevel === 0 && options.runOnly && !excludedSuite(T.onlySuites, label) && !T.onlySuitesAsUserParams) { T.onlySuites.push(label) }
    if (T.deepLevel === 0 && preventsSuiteFromRunning(label)) { return }

    return (T.analyzing ? analizeBlock(label, fn) : describeBlock(label, fn))
  }

  function analizeBlock (label, fn) {
    if (T.deepLevel === 0) {
      var testSuite = describeBlock.bind(this, label, fn)
      T.currentRootDescribeBlock = testSuite
      T.suites.push(testSuite)
      T.isFirstAddedSuite && Meteor.startup(function () { // upon first suite addition, add a callback to startup to run tests
        T.analize() // look at suites structure and prepare the test run (this allows, for example, iit behavior)
        T.run() // run code defined inside suites (describe blocks)
      })
      T.isFirstAddedSuite = false
    }
    T.deepLevel++
    fn()
    T.deepLevel--
  }

  function describeBlock (label, fn) {
    if (T.deepLevel === 0 && preventsSuiteFromRunning(label)) { return }
    T.describeMessages.push(T.message(type, label, T.deepLevel))
    T.deepLevel++
    fn()
    T.itBlockRunLevel = T.deepLevel
    T.describeMessages = []
    T._contextBlocks.forEach(filterFromSameLevel)
    T.deepLevel--
  }

  function filterFromSameLevel (blockName) {
    T[blockName + 'Blocks'] = T[blockName + 'Blocks'].filter(differentLevel)

    function differentLevel (obj) {
      return obj.deepLevel !== T.deepLevel
    }
  }
}

function preventsSuiteFromRunning (label) {
  if (label.label) { label = label.label } // allows to pass { label: '...', fn: ... } as arg
  return excludedSuite(label) || (T.onlySuites.some(isInclusionOnly) && !T.onlySuites.some(included))

  function isInclusionOnly (onlySuite) {
    return !startsWith(onlySuite, '~')
  }

  function included (onlySuite) {
    return onlySuite === label
  }
}

function excludedSuite (label) {
  return T.onlySuites.some(excluded)

  function excluded (onlySuite) {
    if (startsWith(onlySuite, '~')) {
      if (onlySuite.replace('~', '') === label) {
        return true
      }
    }
  }
}

function log (obj) {
  !T.analyzing && console.log(obj) // prevent logging while analyzing the suites
}

function startsWith (str, needle) {
  return str.indexOf(needle) === 0
}

function getCollections () {
  return Object.keys(global)
    .map(toGlobalObject)
    .filter(nonMeteorCollections)
    .concat([Meteor.users])

  function toGlobalObject (key) {
    return global[key]
  }

  function nonMeteorCollections (globalObject) {
    return globalObject instanceof Meteor.Collection
  }
}

function addContextBlocks () {
  T._contextBlocks.forEach(addContextBlock)

  function addContextBlock (blockName) {
    T[blockName + 'Blocks'] = []
    T[blockName] = function (fn) {
      if (T.analyzing) { return }
      T[blockName + 'Blocks'].push({ fn: fn, deepLevel: T.deepLevel })
    }
  }
}

// Global variables are not attached to the `global` object in Meteor packages, so we ignore style checker for this section.
// Details: http://stackoverflow.com/questions/31526454/global-variables-not-being-attached-to-the-global-object-on-meteorjs-packages

/*eslint-disable */
describe = T.describe.bind(T)
context = T.context.bind(T)
it = T.it.bind(T)
iit = T.iit.bind(T)
beforeEach = T.beforeEach.bind(T)
afterEach = T.afterEach.bind(T)
ddescribe = T.ddescribe.bind(T)
ccontext = T.ccontext.bind(T)
/*eslint-enable */

<commit_msg>Allow specifying a container variable for all collections<commit_after>var figures = Npm.require('figures')

T = { // eslint-disable-line
  analize: function () { // this lets us analyze suite's structure to act accordingly later (allowing, for example, iit blocks to work)
    T.analyzing = true
    T.suites.forEach(exec)
    T.analyzing = false
  },
  run: function () {
    if (!process.env.RUN_TESTS) { return } // only run tests when explicitly told so

    var testingDB = new global.MongoInternals.RemoteCollectionDriver(T.testingDbUrl) // create a driver pointing to testing's DB
    getCollections().forEach(pointToTestingDB) // point collections to testing's DB
    getCollections().forEach(removeAll) // erase date on testing DB (though there should be none)

    // Run specs
    T.onlyRootDescribeBlocksForIit.length ? T.onlyRootDescribeBlocksForIit.forEach(exec) : T.suites.forEach(exec) // if there's `iit` blocks, only run those

    // output number of successful over total tests
    log('\n' + (T.itCount + ' tests: ').yellow + (T.successfulItCount + ' passing, ').green + (T.itCount - T.successfulItCount + ' failing.').red)

    getCollections().forEach(pointBackToDevelopDB) // point collections back to development's DB
    _(T.postRunCallbacks).reject(preventsSuiteFromRunning).map(fns).forEach(exec) // allow to run a callback when testing has finished (before possibly ending the process)

    process.env.RUN_TESTS !== 'cont' && process.exit(T.exceptions.length) // end the process unless option is specified

    function pointToTestingDB (collection) {
      collection.latte_original_driver = collection._driver // keep track of original driver, to point back to development's DB once tests have finished
      collection._driver = testingDB
      collection._driver.open(collection._name, collection._connection)
    }

    function pointBackToDevelopDB (collection) {
      collection.latte_original_driver.open(collection._name, collection._connection)
    }
  },
  describe: descriptionBlock('describe'),
  context: descriptionBlock('context'),
  ddescribe: descriptionBlock('describe', { runOnly: true }),
  ccontext: descriptionBlock('context', { runOnly: true }),
  iit: function (label, fn, options) {
    if (T.analyzing) {
      if (!_(T.onlyRootDescribeBlocksForIit).contains(T.currentRootDescribeBlock)) { // only if we have not already added the root describe
        // keeping track of the root describe allows us to run all before/after hooks from the beginning.
        // This also allows to print all labels from the beginning
        T.onlyRootDescribeBlocksForIit.push(T.currentRootDescribeBlock)
      }
    }
    options = options || {}
    T.it(label, fn, _(options).extend({ runOnly: true }))
  },
  it: function (label, fn, options) {
    if (T.analyzing) { return } // don't run `it` blocks when analyzing
    options = options || {}
    var msg = T.message('it', label, T.deepLevel) // generate msg for reports

    if (T.onlyRootDescribeBlocksForIit.length) { // if there's any `iit` block
      if (!options.runOnly) { return } // only run `iit` blocks
      msg = msg.underline
    }

    while (T.describeMessages.length) { // only print `describe` labels if their nested `it` blocks get executed
      console.log(T.describeMessages.shift())
    }

    T.itCount++ // count number of tests, for reports
    try {
      T.itBlockRunLevel = T.deepLevel
      T.beforeEachBlocks.map(fns).forEach(exec) // run beforeEach blocks
      fn()                                      // run assertions in it block
      T.afterEachBlocks.map(fns).forEach(exec)  // run afterEach blocks
      T.successfulItCount++                     // count number of successful tests, for reports
      log((msg + ' ' + figures.tick.green))                 // log into stdout tests label and result
    } catch (e) {
      if (e.message && e.message === "It\'s ok  to display this error, it\'s part of a test -12344321-") { // testing purposes
        T.successfulItCount++                     // count number of successful tests, for reports
      } else { // normal flow
        log(msg + ' ' + figures.cross.red)
        log(e.stack || e)
        T.exceptions.push(e)                      // if `T.exceptions` has any item at the en of the test run, exit code will be != 0
      }
    } finally {
      getCollections().forEach(removeAll)
    }
  },
  message: function (type, label, deepLevel) { // pretty print messages for console report
    var prefix = ''
    if (T.deepLevel === 0) { prefix += '~~~~~~~~'.grey + '\n' }
    prefix += _.range(deepLevel).reduce(addIndentation, '')
    return prefix + type.magenta.bold + ' ' + label.cyan

    function addIndentation (space) {
      return space + '  '
    }
  },
  ignore: function () {},
  suites: [],
  postRunCallbacks: [],
  describeBlocks: [],
  exceptions: [],
  deepLevel: 0,
  horizontalLevel: 0,
  itCount: 0,
  successfulItCount: 0,
  onlyRootDescribeBlocksForIit: [],
  describeMessages: [],
  itBlockRunLevel: 0,
  isFirstAddedSuite: true,
  analyzing: true,
  onlySuites: [],
  _contextBlocks: ['beforeEach', 'afterEach'],
  testingDbUrl: 'mongodb://127.0.0.1:3001/meteor_latte'
}

addContextBlocks() // adds before/after each/all functions

if (process.env.LATTE_SUITES) {
  T.onlySuites = T.onlySuites.concat(JSON.parse(process.env.LATTE_SUITES))
  if (T.onlySuites.length) {
    T.onlySuitesAsUserParams = true // if user specifies some suites to run, only run those
  }
}

function fns (obj) { return obj.fn }

function exec (fn) { fn() }

function removeAll (collection) { collection.remove({}) }

function descriptionBlock (type, options) {
  if (!process.env.RUN_TESTS) { return function () {} } // don't run any test related stuff unless explicitly told so
  options = options || {}

  return function analyzeOrExec (label, fn) {
    if (T.deepLevel === 0 && options.runOnly && !excludedSuite(T.onlySuites, label) && !T.onlySuitesAsUserParams) { T.onlySuites.push(label) }
    if (T.deepLevel === 0 && preventsSuiteFromRunning(label)) { return }

    return (T.analyzing ? analizeBlock(label, fn) : describeBlock(label, fn))
  }

  function analizeBlock (label, fn) {
    if (T.deepLevel === 0) {
      var testSuite = describeBlock.bind(this, label, fn)
      T.currentRootDescribeBlock = testSuite
      T.suites.push(testSuite)
      T.isFirstAddedSuite && Meteor.startup(function () { // upon first suite addition, add a callback to startup to run tests
        T.analize() // look at suites structure and prepare the test run (this allows, for example, iit behavior)
        T.run() // run code defined inside suites (describe blocks)
      })
      T.isFirstAddedSuite = false
    }
    T.deepLevel++
    fn()
    T.deepLevel--
  }

  function describeBlock (label, fn) {
    if (T.deepLevel === 0 && preventsSuiteFromRunning(label)) { return }
    T.describeMessages.push(T.message(type, label, T.deepLevel))
    T.deepLevel++
    fn()
    T.itBlockRunLevel = T.deepLevel
    T.describeMessages = []
    T._contextBlocks.forEach(filterFromSameLevel)
    T.deepLevel--
  }

  function filterFromSameLevel (blockName) {
    T[blockName + 'Blocks'] = T[blockName + 'Blocks'].filter(differentLevel)

    function differentLevel (obj) {
      return obj.deepLevel !== T.deepLevel
    }
  }
}

function preventsSuiteFromRunning (label) {
  if (label.label) { label = label.label } // allows to pass { label: '...', fn: ... } as arg
  return excludedSuite(label) || (T.onlySuites.some(isInclusionOnly) && !T.onlySuites.some(included))

  function isInclusionOnly (onlySuite) {
    return !startsWith(onlySuite, '~')
  }

  function included (onlySuite) {
    return onlySuite === label
  }
}

function excludedSuite (label) {
  return T.onlySuites.some(excluded)

  function excluded (onlySuite) {
    if (startsWith(onlySuite, '~')) {
      if (onlySuite.replace('~', '') === label) {
        return true
      }
    }
  }
}

function log (obj) {
  !T.analyzing && console.log(obj) // prevent logging while analyzing the suites
}

function startsWith (str, needle) {
  return str.indexOf(needle) === 0
}

function getCollections () {
  const globalObject = T.collectionsContainer || global
  return Object.keys(globalObject)
    .map(toGlobalObject)
    .filter(nonMeteorCollections)
    .concat([Meteor.users])

  function toGlobalObject (key) {
    return globalObject[key]
  }

  function nonMeteorCollections (value) {
    return value instanceof Meteor.Collection
  }
}

function addContextBlocks () {
  T._contextBlocks.forEach(addContextBlock)

  function addContextBlock (blockName) {
    T[blockName + 'Blocks'] = []
    T[blockName] = function (fn) {
      if (T.analyzing) { return }
      T[blockName + 'Blocks'].push({ fn: fn, deepLevel: T.deepLevel })
    }
  }
}

// Global variables are not attached to the `global` object in Meteor packages, so we ignore style checker for this section.
// Details: http://stackoverflow.com/questions/31526454/global-variables-not-being-attached-to-the-global-object-on-meteorjs-packages

/*eslint-disable */
describe = T.describe.bind(T)
context = T.context.bind(T)
it = T.it.bind(T)
iit = T.iit.bind(T)
beforeEach = T.beforeEach.bind(T)
afterEach = T.afterEach.bind(T)
ddescribe = T.ddescribe.bind(T)
ccontext = T.ccontext.bind(T)
/*eslint-enable */

<|endoftext|>


<commit_before>const path = require('path');
const debug = require('debug')('email-templates');
const fs = require('fs-extra');
const htmlToText = require('html-to-text');
const I18N = require('@ladjs/i18n');
const autoBind = require('auto-bind');
const nodemailer = require('nodemailer');
const consolidate = require('consolidate');
const previewEmail = require('preview-email');
const _ = require('lodash');
const Promise = require('bluebird');

const getPaths = require('get-paths');
const juiceResources = require('juice-resources-promise');

class Email {
  constructor(config = {}) {
    debug('config passed %O', config);

    // 2.x backwards compatible support
    if (config.juiceOptions) {
      config.juiceResources = config.juiceOptions;
      delete config.juiceOptions;
    }
    if (config.disableJuice) {
      config.juice = false;
      delete config.disableJuice;
    }

    this.config = _.merge(
      {
        views: {
          // directory where email templates reside
          root: path.resolve('emails'),
          options: {
            // default file extension for template
            extension: 'pug',
            map: {},
            engineSource: consolidate
          },
          // locals to pass to templates for rendering
          locals: {
            // pretty is automatically set to `false` for subject/text
            pretty: true
          }
        },
        // <https://nodemailer.com/message/>
        message: {},
        send: !['development', 'test'].includes(process.env.NODE_ENV),
        preview: process.env.NODE_ENV === 'development',
        // <https://github.com/ladjs/i18n>
        // set to an object to configure and enable it
        i18n: false,
        // pass a custom render function if necessary
        render: this.render.bind(this),
        // force text-only rendering of template (disregards template folder)
        textOnly: false,
        // <https://github.com/werk85/node-html-to-text>
        htmlToText: {
          ignoreImage: true
        },
        // <https://github.com/Automattic/juice>
        juice: true,
        juiceResources: {
          preserveImportant: true,
          webResources: {
            relativeTo: path.resolve('build')
          }
        },
        // pass a transport configuration object or a transport instance
        // (e.g. an instance is created via `nodemailer.createTransport`)
        // <https://nodemailer.com/transports/>
        transport: {}
      },
      config
    );

    if (!_.isFunction(this.config.transport.sendMail))
      this.config.transport = nodemailer.createTransport(this.config.transport);

    debug('transformed config %O', this.config);

    autoBind(this);
  }

  // shorthand use of `juiceResources` with the config
  // (mainly for custom renders like from a database)
  juiceResources(html) {
    return juiceResources(html, this.config.juiceResources);
  }

  // a simple helper function that gets the actual file path for the template
  getTemplatePath(view) {
    return new Promise(async (resolve, reject) => {
      try {
        const paths = await getPaths(
          this.config.views.root,
          view,
          this.config.views.options.extension
        );
        const filePath = path.resolve(this.config.views.root, paths.rel);
        resolve({ filePath, paths });
      } catch (err) {
        reject(err);
      }
    });
  }

  // returns true or false if a template exists
  // (uses same look-up approach as `render` function)
  templateExists(view) {
    return new Promise(async resolve => {
      try {
        const { filePath } = await this.getTemplatePath(view);
        const stats = await fs.stat(filePath);
        if (!stats.isFile()) throw new Error(`${filePath} was not a file`);
        resolve(true);
      } catch (err) {
        debug('templateExists', err);
        resolve(false);
      }
    });
  }

  // promise version of consolidate's render
  // inspired by koa-views and re-uses the same config
  // <https://github.com/queckezz/koa-views>
  render(view, locals) {
    return new Promise(async (resolve, reject) => {
      try {
        const { map, engineSource } = this.config.views.options;
        const { filePath, paths } = await this.getTemplatePath(view);
        if (paths.ext === 'html' && !map) {
          const res = await fs.readFile(filePath, 'utf8');
          resolve(res);
        } else {
          const engineName = map && map[paths.ext] ? map[paths.ext] : paths.ext;
          const render = engineSource[engineName];
          if (!engineName || !render)
            return reject(
              new Error(
                `Engine not found for the ".${paths.ext}" file extension`
              )
            );
          // TODO: convert this to a promise based version
          render(filePath, locals, (err, res) => {
            if (err) return reject(err);
            // transform the html with juice using remote paths
            // google now supports media queries
            // https://developers.google.com/gmail/design/reference/supported_css
            if (!this.config.juice) return resolve(res);
            this.juiceResources(res)
              .then(resolve)
              .catch(reject);
          });
        }
      } catch (err) {
        reject(err);
      }
    });
  }

  send(options = {}) {
    options = Object.assign(
      {
        template: '',
        message: {},
        locals: {}
      },
      options
    );

    let { template, message, locals } = options;

    const attachments =
      message.attachments || this.config.message.attachments || [];

    message = _.defaultsDeep(
      {},
      _.omit(this.config.message, 'attachments'),
      _.omit(message, 'attachments')
    );
    locals = _.defaultsDeep({}, this.config.views.locals, locals);

    if (attachments) message.attachments = attachments;

    debug('template %s', template);
    debug('message %O', message);
    debug('locals (keys only): %O', Object.keys(locals));

    return new Promise(async (resolve, reject) => {
      try {
        if (_.isObject(this.config.i18n)) {
          const i18n = new I18N(
            Object.assign({}, this.config.i18n, {
              register: locals
            })
          );

          // support `locals.user.last_locale`
          // (e.g. for <https://lad.js.org>)
          if (_.isObject(locals.user) && _.isString(locals.user.last_locale))
            locals.locale = locals.user.last_locale;

          if (_.isString(locals.locale)) i18n.setLocale(locals.locale);
        }

        let subjectTemplateExists = false;
        let htmlTemplateExists = false;
        let textTemplateExists = false;

        const promises = [
          this.templateExists(`${template}/subject`),
          this.templateExists(`${template}/html`),
          this.templateExists(`${template}/text`)
        ];

        if (template)
          [
            subjectTemplateExists,
            htmlTemplateExists,
            textTemplateExists
          ] = await Promise.all(promises);

        if (!message.subject && subjectTemplateExists)
          message.subject = await this.config.render(
            `${template}/subject`,
            Object.assign({}, locals, { pretty: false })
          );

        if (!message.html && htmlTemplateExists)
          message.html = await this.config.render(`${template}/html`, locals);

        if (
          (!htmlTemplateExists || !this.config.htmlToText) &&
          !message.text &&
          textTemplateExists
        )
          message.text = await this.config.render(
            `${template}/text`,
            Object.assign({}, locals, { pretty: false })
          );
        else if (this.config.htmlToText && message.html)
          // we'd use nodemailer-html-to-text plugin
          // but we really don't need to support cid
          // <https://github.com/andris9/nodemailer-html-to-text>
          message.text = htmlToText.fromString(
            message.html,
            this.config.htmlToText
          );

        // if we only want a text-based version of the email
        if (this.config.textOnly) delete message.html;

        if (this.config.preview) {
          debug('using `preview-email` to preview email');
          await previewEmail(message);
        }

        if (!this.config.send) {
          debug('send disabled so we are ensuring JSONTransport');
          // <https://github.com/nodemailer/nodemailer/issues/798>
          // if (this.config.transport.name !== 'JSONTransport')
          this.config.transport = nodemailer.createTransport({
            jsonTransport: true
          });
        }

        const res = await this.config.transport.sendMail(message);
        debug('message sent');
        resolve(res);
      } catch (err) {
        reject(err);
      }
    });
  }
}

module.exports = Email;
<commit_msg>Added i18n localization to render method, closes #261<commit_after>const path = require('path');
const debug = require('debug')('email-templates');
const fs = require('fs-extra');
const htmlToText = require('html-to-text');
const I18N = require('@ladjs/i18n');
const autoBind = require('auto-bind');
const nodemailer = require('nodemailer');
const consolidate = require('consolidate');
const previewEmail = require('preview-email');
const _ = require('lodash');
const Promise = require('bluebird');

const getPaths = require('get-paths');
const juiceResources = require('juice-resources-promise');

class Email {
  constructor(config = {}) {
    debug('config passed %O', config);

    // 2.x backwards compatible support
    if (config.juiceOptions) {
      config.juiceResources = config.juiceOptions;
      delete config.juiceOptions;
    }
    if (config.disableJuice) {
      config.juice = false;
      delete config.disableJuice;
    }

    this.config = _.merge(
      {
        views: {
          // directory where email templates reside
          root: path.resolve('emails'),
          options: {
            // default file extension for template
            extension: 'pug',
            map: {},
            engineSource: consolidate
          },
          // locals to pass to templates for rendering
          locals: {
            // pretty is automatically set to `false` for subject/text
            pretty: true
          }
        },
        // <https://nodemailer.com/message/>
        message: {},
        send: !['development', 'test'].includes(process.env.NODE_ENV),
        preview: process.env.NODE_ENV === 'development',
        // <https://github.com/ladjs/i18n>
        // set to an object to configure and enable it
        i18n: false,
        // pass a custom render function if necessary
        render: this.render.bind(this),
        // force text-only rendering of template (disregards template folder)
        textOnly: false,
        // <https://github.com/werk85/node-html-to-text>
        htmlToText: {
          ignoreImage: true
        },
        // <https://github.com/Automattic/juice>
        juice: true,
        juiceResources: {
          preserveImportant: true,
          webResources: {
            relativeTo: path.resolve('build')
          }
        },
        // pass a transport configuration object or a transport instance
        // (e.g. an instance is created via `nodemailer.createTransport`)
        // <https://nodemailer.com/transports/>
        transport: {}
      },
      config
    );

    if (!_.isFunction(this.config.transport.sendMail))
      this.config.transport = nodemailer.createTransport(this.config.transport);

    debug('transformed config %O', this.config);

    autoBind(this);
  }

  // shorthand use of `juiceResources` with the config
  // (mainly for custom renders like from a database)
  juiceResources(html) {
    return juiceResources(html, this.config.juiceResources);
  }

  // a simple helper function that gets the actual file path for the template
  getTemplatePath(view) {
    return new Promise(async (resolve, reject) => {
      try {
        const paths = await getPaths(
          this.config.views.root,
          view,
          this.config.views.options.extension
        );
        const filePath = path.resolve(this.config.views.root, paths.rel);
        resolve({ filePath, paths });
      } catch (err) {
        reject(err);
      }
    });
  }

  // returns true or false if a template exists
  // (uses same look-up approach as `render` function)
  templateExists(view) {
    return new Promise(async resolve => {
      try {
        const { filePath } = await this.getTemplatePath(view);
        const stats = await fs.stat(filePath);
        if (!stats.isFile()) throw new Error(`${filePath} was not a file`);
        resolve(true);
      } catch (err) {
        debug('templateExists', err);
        resolve(false);
      }
    });
  }

  // promise version of consolidate's render
  // inspired by koa-views and re-uses the same config
  // <https://github.com/queckezz/koa-views>
  render(view, locals) {
    return new Promise(async (resolve, reject) => {
      try {
        const { map, engineSource } = this.config.views.options;
        const { filePath, paths } = await this.getTemplatePath(view);
        if (paths.ext === 'html' && !map) {
          const res = await fs.readFile(filePath, 'utf8');
          resolve(res);
        } else {
          const engineName = map && map[paths.ext] ? map[paths.ext] : paths.ext;
          const render = engineSource[engineName];
          if (!engineName || !render)
            return reject(
              new Error(
                `Engine not found for the ".${paths.ext}" file extension`
              )
            );

          if (_.isObject(this.config.i18n)) {
            const i18n = new I18N(
              Object.assign({}, this.config.i18n, {
                register: locals
              })
            );

            // support `locals.user.last_locale`
            // (e.g. for <https://lad.js.org>)
            if (_.isObject(locals.user) && _.isString(locals.user.last_locale))
              locals.locale = locals.user.last_locale;

            if (_.isString(locals.locale)) i18n.setLocale(locals.locale);
          }

          // TODO: convert this to a promise based version
          render(filePath, locals, (err, res) => {
            if (err) return reject(err);
            // transform the html with juice using remote paths
            // google now supports media queries
            // https://developers.google.com/gmail/design/reference/supported_css
            if (!this.config.juice) return resolve(res);
            this.juiceResources(res)
              .then(resolve)
              .catch(reject);
          });
        }
      } catch (err) {
        reject(err);
      }
    });
  }

  send(options = {}) {
    options = Object.assign(
      {
        template: '',
        message: {},
        locals: {}
      },
      options
    );

    let { template, message, locals } = options;

    const attachments =
      message.attachments || this.config.message.attachments || [];

    message = _.defaultsDeep(
      {},
      _.omit(this.config.message, 'attachments'),
      _.omit(message, 'attachments')
    );
    locals = _.defaultsDeep({}, this.config.views.locals, locals);

    if (attachments) message.attachments = attachments;

    debug('template %s', template);
    debug('message %O', message);
    debug('locals (keys only): %O', Object.keys(locals));

    return new Promise(async (resolve, reject) => {
      try {
        let subjectTemplateExists = false;
        let htmlTemplateExists = false;
        let textTemplateExists = false;

        const promises = [
          this.templateExists(`${template}/subject`),
          this.templateExists(`${template}/html`),
          this.templateExists(`${template}/text`)
        ];

        if (template)
          [
            subjectTemplateExists,
            htmlTemplateExists,
            textTemplateExists
          ] = await Promise.all(promises);

        if (!message.subject && subjectTemplateExists)
          message.subject = await this.config.render(
            `${template}/subject`,
            Object.assign({}, locals, { pretty: false })
          );

        if (!message.html && htmlTemplateExists)
          message.html = await this.config.render(`${template}/html`, locals);

        if (
          (!htmlTemplateExists || !this.config.htmlToText) &&
          !message.text &&
          textTemplateExists
        )
          message.text = await this.config.render(
            `${template}/text`,
            Object.assign({}, locals, { pretty: false })
          );
        else if (this.config.htmlToText && message.html)
          // we'd use nodemailer-html-to-text plugin
          // but we really don't need to support cid
          // <https://github.com/andris9/nodemailer-html-to-text>
          message.text = htmlToText.fromString(
            message.html,
            this.config.htmlToText
          );

        // if we only want a text-based version of the email
        if (this.config.textOnly) delete message.html;

        if (this.config.preview) {
          debug('using `preview-email` to preview email');
          await previewEmail(message);
        }

        if (!this.config.send) {
          debug('send disabled so we are ensuring JSONTransport');
          // <https://github.com/nodemailer/nodemailer/issues/798>
          // if (this.config.transport.name !== 'JSONTransport')
          this.config.transport = nodemailer.createTransport({
            jsonTransport: true
          });
        }

        const res = await this.config.transport.sendMail(message);
        debug('message sent');
        resolve(res);
      } catch (err) {
        reject(err);
      }
    });
  }
}

module.exports = Email;
<|endoftext|>


<commit_before>import * as hooks from './hooks';
import service from './service';
import mongoose from 'mongoose';

export { hooks };
export { mongoose };
export { service };
export default service;<commit_msg>no longer exposing mongoose<commit_after>import * as hooks from './hooks';
import service from './service';

export { hooks };
export { service };
export default service;<|endoftext|>


<commit_before>/*
 * GCRand
 *      A new Marsaglia's KISS -style PRNG
 *      - Period ~2^^96, enough to generate 2^^32 random numbers with a 32-bit seed
 *      - Passes all BigCrush tests
 *      - Performance is good
 *              It's 10% faster than a JavaScript Mersenne Twister I found for
 *              long output and much faster for short output
 *      - No multiply/divide/modulus instructions required
 *  - Completely avoids floating-point operations, so it should be fairly fast
 *    and portable across all platforms
 *  - For additional performance in inner loops use the cached version below
 *
 * Technical contact: Christopher A. Taylor <chris@gameclosure.com>
 */

// Define GCRand class
function GCRand(seed) {
        this.init(seed);
}

// Initialize the random number generator from an optional seed
GCRand.prototype.init = function(seed) {

        // If seed was not specified,
        if (typeof(seed) != 'number') {
                var mrx = Math.random() * 4294967296.0;
                var mry = new Date().getTime();
                seed = mrx ^ mry;
        }

        // XOR Shift
        x = seed ^ 1453667877;

        // Weyl Generator
        y = seed ^ 1223235218;

        // Add-With-Carry
        z = 2686646964 >>> 0;
        w = 3741327162 >>> 0;
        c = 0;
}

// Iterate state object returning the next 32-bit unsigned random value
GCRand.prototype.uint32 = function() {
        // XOR Shift
        var x = this.x;
        x ^= x << 5;
        x ^= x >>> 7;
        x ^= x << 22;
        this.x = x;

        // Weyl Generator
        var y = this.y;
        y += 2654435769;
        this.y = y >>> 0;

        // Add-With-Carry
        var w = this.w;
        var t = (this.z + w + this.c) >>> 0;
        this.z = w;
        this.c = t >>> 31;
        w = t & 2147483647;
        this.w = w;

        return (x + y + w) >>> 0;
}

// Produce a 31-bit integer number in the range [0 ... 0x7fffffff] inclusive
GCRand.prototype.uint31 = function() {
        return this.uint32() >>> 1;
}

// Produce a 32-bit floating-point number in the range [0.0 ... 1.0] inclusive
GCRand.prototype.random = function() {
        return this.uint32() * (1.0/4294967296.0);
}

// Produce a floating-point number in the range [a ... b] inclusive
GCRand.prototype.range_real = function(a, b) {
        return ( this.uint32() * (1.0/4294967296.0) ) * (b - a) + a;
}

// Produce an integer number in the range [a ... b] inclusive
GCRand.prototype.range_int = function(a, b) {
        return ( ( this.uint32() * (1.0/4294967296.0) ) * (b - a) + a + 0.5 ) >>> 0;
}

// Produce a 53-bit floating-point number according to a normal distribution
// with mean = 0 and variance = 1, using the Box-Mulder (trigonometric) method
GCRand.prototype.normal = function() {
        var u1 = 0,
                u2 = 0;

        while (u1 * u2 == 0.0) {
                u1 = this.random();
                u2 = this.random();
        }

        return Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
}

// Produce a 53-bit floating-point number according to a Gaussian distribution
// with your specified mean and variance
GCRand.prototype.gaussian = function(mean, stddev) {
        return stddev * this.normal() + mean;
}

// Same as kiss96_gauss except it also clamps to a specified range
GCRand.prototype.gaussian_clamp = function(mean, stddev, clamp_lo, clamp_hi) {
        var x = stddev * this.normal() + mean;

        // Clamp to range
        if (x < clamp_lo) {
                x = clamp_lo;
        } else {
                if (x > clamp_hi) {
                        x = clamp_hi;
                }
        }

        return x;
}

/*
 * GCFastRand
 * Caches the random number generator output and loops over it for speed.
 *      - Provide a function to call to generate the numbers
 *      - Or accept the default of behaving like Math.random()
 *      - Invoke it like GCFastRand.random()
 */

function GCFastRand(prng_function, count) {

        if (!prng_function) {
                prng_function = Math.random;
        }

        if (typeof(count) != 'number') {
                count = 269; // A prime number to improve access randomness
        }

        this.count = count;
        this.index = 0;
        this.vector = [];
        for (var i = 0; i < count; ++i) {
                this.vector[i] = prng_function();
        }
}

GCFastRand.prototype.random = function() {
        var index = this.index++;
        if (index >= this.count) {
                index = 0;
                this.index = 1;
        }

        return this.vector[index];
}

// Exports
exports.Random = GCRand;
exports.FastRandom = GCFastRand;
<commit_msg>Even BETTER random math<commit_after>/*
 * GCRand
 * 	A new Marsaglia's KISS -style PRNG
 * 	- Period ~2^^96, enough to generate 2^^32 random numbers with a 32-bit seed
 * 	- Passes all BigCrush tests
 * 	- Performance is good
 * 		It's 10% faster than a JavaScript Mersenne Twister I found for
 * 		long output and much faster for short output
 * 	- No multiply/divide/modulus instructions required
 *  - Completely avoids floating-point operations, so it should be fairly fast
 *    and portable across all platforms
 *  - For additional performance in inner loops use the cached version below
 *
 * Technical contact: Christopher A. Taylor <chris@gameclosure.com>
 */

var RNG = Class(function() {

	// Initialize the random number generator from an optional seed (opts.seed)
	this.init = function(seed) {
		// If seed was not specified,
		if (typeof(seed) != 'number') {
			var mrx = Math.random() * 4294967296.0;
			var mry = new Date().getTime();
			seed = mrx ^ mry;
		}

		// XOR Shift
		this.x = seed ^ 1453667877;

		// Weyl Generator
		this.y = seed ^ 1223235218;

		// Add-With-Carry
		this.z = 2686646964 >>> 0;
		this.w = 3741327162 >>> 0;
		this.c = 0;
	}

	// Iterate state object returning the next 32-bit unsigned random value
	this.uint32 = function() {
		// XOR Shift
		var x = this.x;
		x ^= x << 5;
		x ^= x >>> 7;
		x ^= x << 22;
		this.x = x;

		// Weyl Generator
		var y = this.y;
		y += 2654435769;
		this.y = y >>> 0;

		// Add-With-Carry
		var w = this.w;
		var t = (this.z + w + this.c) >>> 0;
		this.z = w;
		this.c = t >>> 31;
		w = t & 2147483647;
		this.w = w;

		return (x + y + w) >>> 0;
	}

	// Produce a 31-bit integer number in the range [0 ... 0x7fffffff] inclusive
	this.uint31 = function() {
		return this.uint32() >>> 1;
	}

	// Produce a 32-bit floating-point number in the range [0.0 ... 1.0] inclusive
	this.random = function() {
		return this.uint32() * (1.0/4294967296.0);
	}

	// Produce a floating-point number in the range [a ... b] inclusive
	this.range_real = function(a, b) {
		return ( this.uint32() * (1.0/4294967296.0) ) * (b - a) + a;
	}

	// Produce an integer number in the range [a ... b] inclusive
	this.range_int = function(a, b) {
		return ( ( this.uint32() * (1.0/4294967296.0) ) * (b - a) + a + 0.5 ) >>> 0;
	}

	// Produce a 53-bit floating-point number according to a normal distribution
	// with mean = 0 and variance = 1, using the Box-Mulder (trigonometric) method
	this.normal = function() {
		var u1 = 0,
			u2 = 0;

		while (u1 * u2 == 0.0) {
			u1 = this.random();
			u2 = this.random();
		}

		return Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
	}

	// Produce a 53-bit floating-point number according to a Gaussian distribution
	// with your specified mean and variance
	this.gaussian = function(mean, stddev) {
		return stddev * this.normal() + mean;
	}

	// Same as kiss96_gauss except it also clamps to a specified range
	this.gaussian_clamp = function(mean, stddev, clamp_lo, clamp_hi) {
		var x = stddev * this.normal() + mean;

		// Clamp to range
		if (x < clamp_lo) {
			x = clamp_lo;
		} else {
			if (x > clamp_hi) {
				x = clamp_hi;
			}
		}

		return x;
	}
});

/**
 * Module API.
 */

exports = new RNG(Date.now());
exports.RNG = RNG;<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
var _path = require('path'),
	http = require('http'),
	url = require('url'),
	express = require('express');

var rode = (function () {
	var self = {};
	var binCommand = false;
	var config;
	var coreConfig;
	var rootPath;
	var started = false;

	self.express = express;
	self.app = self.express();

	/**
	 * Start Rode App
	 *
	 * @param {string} path
	 * @param {Function} cb
	 */
	self.start = function (path, cb) {
		rootPath = path;
		if (!coreConfig) {
			self.getCoreConfig();
		}
		if (!config) {
			self.getConfig();
		}
		started = true;
		cb(null);
	};

	/**
	 * Start HTTP Server
	 *
	 * @param {Function} cb
	 */
	self.startServer = function (cb) {
		createRoutes(function () {
			self.view.compile(function (err) {
				if (err) throw err;
				http.createServer(self.app).listen(self.app.get('port'), cb);
			});
		});
	};

	/**
	 * Get Root Path
	 *
	 * @param [dir]
	 * @returns {string}
	 */
	self.getRootPath = function (dir) {
		var path = rootPath;
		if (!path) {
			return null;
		}
		if (dir) {
			path = _path.join(path, dir);
		}
		return _path.normalize(path + '/');
	};

	/**
	 * Set Root Path
	 *
	 * @param {string} path
	 */
	self.setRootPath = function (path) {
		rootPath = path;
	};

	/**
	 *
	 * @returns {string}
	 */
	self.getCorePath = function () {
		return _path.normalize(__dirname + '/');
	};

	/**
	 * Get path from a name.
	 *
	 * @param {string} name
	 * @returns {string}
	 */
	self.getPath = function (name) {
		switch (name) {
			case 'root':
				return self.getRootPath();
				break;
			case 'src':
				return self.getRootPath(self.getConfig().srcDir);
				break;
			case 'views':
				return self.getRootPath(self.getConfig().views.dir);
				break;
			case 'statics':case 'public':
				return self.getRootPath(self.getConfig().statics.dir);
				break;
			case 'images':
				return _path.join(self.getPath('statics'), self.getConfig().statics.images);
				break;
			case 'js':case 'javascript':
				return _path.join(self.getPath('statics'), self.getConfig().statics.js);
				break;
			case 'css':case 'stylesheets':
				return _path.join(self.getPath('statics'), self.getConfig().statics.css);
				break;
			default:
				return '';
		}
	};

	/**
	 *
	 * @param  {string} [env]
	 * @returns {Object}
	 */
	self.getConfig = function (env) {
		if (!config) {
			configger(require(self.getRootPath() + 'config/config')(env));
		}
		return config;
	};

	/**
	 *
	 * @param {string} [env]
	 * @param {boolean} [force]
	 * @returns {Object}
	 */
	self.getCoreConfig = function (env, force) {
		if (!coreConfig) {
			coreConfig = require(self.getCorePath() + 'config/config')(env);

			// Config Packages as soon as we have CoreConfig
			self.packages = require(self.getCoreConfig().srcDir + '/Component/Core/Packages');
		}
		if (force) {
			configger(coreConfig);
			self.getDb();
		}
		return coreConfig;
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [ctrl]
	 * @returns {Controller}
	 */
	self.getController = function (pack, ctrl) {
		if (!ctrl) {
			ctrl = pack;
		}
		return require(self.packages.getPath(pack) + '/Controller/' + ctrl + 'Controller');
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [ctrl]
	 * @returns {Controller}
	 */
	self.getCoreController = function (pack, ctrl) {
		if (!ctrl) {
			ctrl = pack;
		}
		return require(self.packages.getCorePath(pack) + '/Controller/' + ctrl + 'Controller');
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [model]
	 * @returns {Model}
	 */
	self.getModel = function (pack, model) {
		if (!model) {
			model = pack
		}
		return require(self.packages.getPath(pack) + '/Model/' + model);
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [model]
	 * @returns {Model}
	 */
	self.getCoreModel = function (pack, model) {
		if (!model) {
			model = pack;
		}
		return require(self.packages.getCorePath(pack) + '/Model/' + model);
	};

	/**
	 *
	 * @param {string} type
	 * @param {string} name
	 * @returns {Object}
	 */
	self.getCoreComponent = function (type, name) {
		return require(self.packages.getCorePath('Component') + '/' + type + '/' + name);
	};

	/**
	 *
	 * @returns {Model}
	 */
	self.getBaseModel = function () {
		return self.getCoreModel('Abstract', 'Model');
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [name]
	 */
	self.getEntity = function (pack, name) {
		if (!name) {
			name = pack;
		}
		return require(self.packages.getPath(pack) + '/Entity/' + name);
	};

	/**
	 *
	 * @returns {Mongo}
	 */
	self.getDb = function () {
		if (binCommand) {
			return null;
		}
		var mongo = self.getCoreComponent('DB', 'Mongo');
		return mongo.getDb();
	};

	/**
	 *
	 * @param {string} pack
	 * @returns {Controller}
	 */
	self.getRouter = function (pack) {
		var routerCtrl = self.getCoreController('Router');
		return routerCtrl(pack);
	};

	/**
	 * Setter for BinCommand
	 *
	 * @param {boolean} bin
	 */
	self.setBinCommand = function (bin) {
		binCommand = bin;
	};

	/**
	 * Check if rode is started
	 *
	 * @returns {boolean}
	 */
	self.isStarted = function () {
		return started;
	};

	/**
	 * App Config
	 *
	 * @param conf
	 */
	var configger = function (conf) {
		config = conf;

        // Create rode models
        self.Model = self.getCoreModel('Abstract', 'Model');

		// Config Port
		self.app.set('port', config.port || 3000);

		// Config Host
		if (config.baseUri && !config.host) {
			config.host = url.parse(config.baseUri).host;
		}

		// Config Views
		self.view = self.getCoreController('Views');
		if (config.views) {
			self.view.configEngine();
		}

		// Config Favicon
		if (config.favicon) {
			self.app.use(config.favicon);
		}

		// Config Logger
		if (config.logger) {
			self.app.use(config.logger);
		}

		// Config CSS
		switch (config.css) {
			case 'less':
				self.app.use(require('less-middleware')(
					{
						src: self.getPath('statics'),
						compress: rode.env === 'production'
					}
				));
				break;
			case 'stylus':
				self.app.use(require('stylus').middleware(self.getPath('statics')));
		}

		// Config Statics
		if (config.statics && config.statics.dir) {
			self.app.use(self.express.static(self.getPath('statics')));
		}

		// Config Error Handler
		if (config.errorHandler) {
			self.app.use(self.express.errorHandler());
		}

		// Config BodyParser
		if (config.bodyParser) {
			self.app.use(self.express.bodyParser());
		}

		// Config MongoDB
		if (config.mongo && config.mongo.autoconnect) {
			self.getDb();
		}
	};

	/**
	 *
	 * @param {Function} cb
	 */
	var createRoutes = function (cb) {
		if (binCommand) {
			cb(null);
			return;
		}
		var Routing = self.getCoreController('Router', 'Routing');
		Routing(cb);
	};

	return self;
})();

module.exports = rode;<commit_msg>Marked old methods as deprecated<commit_after>/**
 * Module dependencies
 */
var _path = require('path'),
	http = require('http'),
	url = require('url'),
	express = require('express');

var rode = (function () {
	var self = {};
	var binCommand = false;
	var config;
	var coreConfig;
	var rootPath;
	var started = false;

	self.express = express;
	self.app = self.express();

	/**
	 * Start Rode App
	 *
	 * @param {string} path
	 * @param {Function} cb
	 */
	self.start = function (path, cb) {
		rootPath = path;
		if (!coreConfig) {
			self.getCoreConfig();
		}
		if (!config) {
			self.getConfig();
		}
		started = true;
		cb(null);
	};

	/**
	 * Start HTTP Server
	 *
	 * @param {Function} cb
	 */
	self.startServer = function (cb) {
		createRoutes(function () {
			self.view.compile(function (err) {
				if (err) throw err;
				http.createServer(self.app).listen(self.app.get('port'), cb);
			});
		});
	};

	/**
	 * Get Root Path
	 *
	 * @param [dir]
	 * @returns {string}
	 */
	self.getRootPath = function (dir) {
		var path = rootPath;
		if (!path) {
			return null;
		}
		if (dir) {
			path = _path.join(path, dir);
		}
		return _path.normalize(path + '/');
	};

	/**
	 * Set Root Path
	 *
	 * @param {string} path
	 */
	self.setRootPath = function (path) {
		rootPath = path;
	};

	/**
	 *
	 * @returns {string}
	 */
	self.getCorePath = function () {
		return _path.normalize(__dirname + '/');
	};

	/**
	 * Get path from a name.
	 *
	 * @param {string} name
	 * @returns {string}
	 */
	self.getPath = function (name) {
		switch (name) {
			case 'root':
				return self.getRootPath();
				break;
			case 'src':
				return self.getRootPath(self.getConfig().srcDir);
				break;
			case 'views':
				return self.getRootPath(self.getConfig().views.dir);
				break;
			case 'statics':case 'public':
				return self.getRootPath(self.getConfig().statics.dir);
				break;
			case 'images':
				return _path.join(self.getPath('statics'), self.getConfig().statics.images);
				break;
			case 'js':case 'javascript':
				return _path.join(self.getPath('statics'), self.getConfig().statics.js);
				break;
			case 'css':case 'stylesheets':
				return _path.join(self.getPath('statics'), self.getConfig().statics.css);
				break;
			default:
				return '';
		}
	};

	/**
	 *
	 * @param  {string} [env]
	 * @returns {Object}
	 */
	self.getConfig = function (env) {
		if (!config) {
			configger(require(self.getRootPath() + 'config/config')(env));
		}
		return config;
	};

	/**
	 *
	 * @param {string} [env]
	 * @param {boolean} [force]
	 * @returns {Object}
	 */
	self.getCoreConfig = function (env, force) {
		if (!coreConfig) {
			coreConfig = require(self.getCorePath() + 'config/config')(env);

			// Config Packages as soon as we have CoreConfig
			self.packages = require(self.getCoreConfig().srcDir + '/Component/Core/Packages');
		}
		if (force) {
			configger(coreConfig);
			self.getDb();
		}
		return coreConfig;
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [ctrl]
	 * @returns {Controller}
	 */
	self.getController = function (pack, ctrl) {
		if (!ctrl) {
			ctrl = pack;
		}
		return require(self.packages.getPath(pack) + '/Controller/' + ctrl + 'Controller');
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [ctrl]
	 * @returns {Controller}
	 */
	self.getCoreController = function (pack, ctrl) {
		if (!ctrl) {
			ctrl = pack;
		}
		return require(self.packages.getCorePath(pack) + '/Controller/' + ctrl + 'Controller');
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [model]
	 * @returns {Model}
	 */
	self.getModel = function (pack, model) {
		if (!model) {
			model = pack
		}
		return require(self.packages.getPath(pack) + '/Model/' + model);
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [model]
	 * @returns {Model}
	 */
	self.getCoreModel = function (pack, model) {
		if (!model) {
			model = pack;
		}
		return require(self.packages.getCorePath(pack) + '/Model/' + model);
	};

	/**
	 *
	 * @param {string} type
	 * @param {string} name
	 * @returns {Object}
	 */
	self.getCoreComponent = function (type, name) {
		return require(self.packages.getCorePath('Component') + '/' + type + '/' + name);
	};

	/**
	 * @deprecated Use rode.Model instead
	 */
	self.getBaseModel = function () {
		return self.Model;
	};

	/**
	 *
	 * @param {string} pack
	 * @param {string} [name]
	 */
	self.getEntity = function (pack, name) {
		if (!name) {
			name = pack;
		}
		return require(self.packages.getPath(pack) + '/Entity/' + name);
	};

	/**
	 *
	 * @returns {Mongo}
	 */
	self.getDb = function () {
		if (binCommand) {
			return null;
		}
		var mongo = self.getCoreComponent('DB', 'Mongo');
		return mongo.getDb();
	};

	/**
	 *
	 * @param {string} pack
	 * @returns {Controller}
	 */
	self.getRouter = function (pack) {
		var routerCtrl = self.getCoreController('Router');
		return routerCtrl(pack);
	};

	/**
	 * Setter for BinCommand
	 *
	 * @param {boolean} bin
	 */
	self.setBinCommand = function (bin) {
		binCommand = bin;
	};

	/**
	 * Check if rode is started
	 *
	 * @returns {boolean}
	 */
	self.isStarted = function () {
		return started;
	};

	/**
	 * App Config
	 *
	 * @param conf
	 */
	var configger = function (conf) {
		config = conf;

        // Create rode models
        self.Model = self.getCoreModel('Abstract', 'Model');

		// Config Port
		self.app.set('port', config.port || 3000);

		// Config Host
		if (config.baseUri && !config.host) {
			config.host = url.parse(config.baseUri).host;
		}

		// Config Views
		self.view = self.getCoreController('Views');
		if (config.views) {
			self.view.configEngine();
		}

		// Config Favicon
		if (config.favicon) {
			self.app.use(config.favicon);
		}

		// Config Logger
		if (config.logger) {
			self.app.use(config.logger);
		}

		// Config CSS
		switch (config.css) {
			case 'less':
				self.app.use(require('less-middleware')(
					{
						src: self.getPath('statics'),
						compress: rode.env === 'production'
					}
				));
				break;
			case 'stylus':
				self.app.use(require('stylus').middleware(self.getPath('statics')));
		}

		// Config Statics
		if (config.statics && config.statics.dir) {
			self.app.use(self.express.static(self.getPath('statics')));
		}

		// Config Error Handler
		if (config.errorHandler) {
			self.app.use(self.express.errorHandler());
		}

		// Config BodyParser
		if (config.bodyParser) {
			self.app.use(self.express.bodyParser());
		}

		// Config MongoDB
		if (config.mongo && config.mongo.autoconnect) {
			self.getDb();
		}
	};

	/**
	 *
	 * @param {Function} cb
	 */
	var createRoutes = function (cb) {
		if (binCommand) {
			cb(null);
			return;
		}
		var Routing = self.getCoreController('Router', 'Routing');
		Routing(cb);
	};

	return self;
})();

module.exports = rode;<|endoftext|>


<commit_before>if (typeof window === 'undefined') {
    /* in node.js */
    var requirejs = require('requirejs');

    requirejs.config({
        //Pass the top-level main.js/index.js require
        //function to requirejs so that node modules
        //are loaded relative to the top-level JS file.
        nodeRequire: require,
	paths: %%PATHS%%
    });

    requirejs([%%MODULE_PATHS_LIST%%], function(%%MODULE_NAMES_LIST%%) {
        %%MAIN%%
    });
} else {
    require.config({
	paths: %%PATHS%%
    });

    /* in browsers*/
    require([%%MODULE_PATHS_LIST%%], function(%%MODULE_NAMES_LIST%%) {
        %%MAIN%%
    });
}
<commit_msg>main template jetzt gepusht<commit_after>if (typeof window === 'undefined') {
    /* in node.js */
    var requirejs = require('requirejs');

    requirejs.config({
        //Pass the top-level main.js/index.js require
        //function to requirejs so that node modules
        //are loaded relative to the top-level JS file.
        nodeRequire: require,
	paths: %%STD_PATH%%
    });

    requirejs([%%MODULE_PATHS_LIST%%], function(%%MODULE_NAMES_LIST%%) {
        %%MAIN%%
    });
} else {
    require.config({
	paths: %%STD_URL%%
    });

    /* in browsers*/
    require([%%MODULE_PATHS_LIST%%], function(%%MODULE_NAMES_LIST%%) {
        %%MAIN%%
    });
}
<|endoftext|>


<commit_before>import React from 'react';
import 'babel/polyfill';

import Logo from './components/logo'
import SocialButtons from './components/social-buttons'
import Navigation from './components/navigation'
import Home from './components/home';
import Footer from './components/footer';

import AppUrl from './appurl'

class Page {
  render() {

    let appUrl = new AppUrl();
    const siteComponent = this.props.siteComponent;

    return (

          <Logo/>
          <SocialButtons/>
          <Navigation appUrl={appUrl} />

        {siteComponent}

    )
  }
}

const rerender = (siteComponent = <Home />) => {
  React.render(<Page siteComponent={siteComponent} />, document.getElementById('app'));
};

import {parse as parseUrl} from 'url';
import Chronicle from './lexicon/chronicle'

window.addEventListener('hashchange', ({newURL: newUrl}) => {
  const parsedUrl = parseUrl(newUrl);
  processUrl(parsedUrl);
});

function processUrl(parsedUrl) {
  if (parsedUrl && parsedUrl.hash && parsedUrl.hash.startsWith('#/')) {
    if (parsedUrl.hash.match(/^#\/chronicle/)) {
      Chronicle.componentWithData((chronicleComponent) => {
        rerender(chronicleComponent);
      });
    }
  } else {
    rerender(<Home />);
  }
}

processUrl();
<commit_msg>Hook up the people component.<commit_after>import React from 'react';
import 'babel/polyfill';

import Logo from './components/logo'
import SocialButtons from './components/social-buttons'
import Navigation from './components/navigation'
import Home from './components/home';
import Footer from './components/footer';

import AppUrl from './appurl'

class Page {
  render() {

    let appUrl = new AppUrl();
    const siteComponent = this.props.siteComponent;

    return (

          <Logo/>
          <SocialButtons/>
          <Navigation appUrl={appUrl} />

        {siteComponent}

    )
  }
}

const rerender = (siteComponent = <Home />) => {
  React.render(<Page siteComponent={siteComponent} />, document.getElementById('app'));
};

import {parse as parseUrl} from 'url';
import Chronicle from './lexicon/chronicle'
import People from './lexicon/people'

window.addEventListener('hashchange', ({newURL: newUrl}) => {
  const parsedUrl = parseUrl(newUrl);
  processUrl(parsedUrl);
});

function processUrl(parsedUrl) {
  if (parsedUrl && parsedUrl.hash && parsedUrl.hash.startsWith('#/')) {
    if (parsedUrl.hash.match(/^#\/chronicle/)) {
      Chronicle.componentWithData((chronicleComponent) => {
        rerender(chronicleComponent);
      });
    }
    if (parsedUrl.hash.match(/^#\/people/)) {
      People.componentWithData((peopleComponent) => {
        rerender(peopleComponent);
      });
    }
  } else {
    rerender(<Home />);
  }
}

processUrl();
<|endoftext|>


<commit_before>let spawnFunctions = require('spawn');

/**
check for sources that are not known in this room and add
them to the list of possible sourceAssignments for this room.
@param {Room} room
*/
function checkForSources(room) {
  if (!room.memory.sourceAssignments) {
    room.memory.sourceAssignments = {};

    _.forEach(room.find(FIND_SOURCES), (source) => {
      if (!room.memory.sourceAssignments[source.id]) {
        room.memory.sourceAssignments[source.id] = "none";
      }
    });
  }
}

/**
Check if a source is missing a miner.
@param {Room} room
*/
function findSourceIdMissingMiner(room) {
  let result = 0;

  _.forEach(room.memory.sourceAssignments, (creepName, sourceId) => {
    if (!Game.creeps[creepName]) {
      result = sourceId;
    }
  });

  return result;
}

/**
Build a miner for the given room and sourceId.
@param {Room} room
@param {string} sourceId
@param {Spawn} spawn
@param {array} body = [WORK, WORK, CARRY, MOVE]
@param {String} name = undefined
*/
function buildMiner(room, sourceId, spawn, body = undefined, name = undefined) {
  if (sourceId) {
    let newCreepName = spawnFunctions.createCreepWithMemory(spawn, {
      role: "miner",
      assignedSourceId: sourceId
    }, body, name);

    if (newCreepName != ERR_NOT_ENOUGH_ENERGY && newCreepName != ERR_BUSY) {
      room.memory.sourceAssignments[sourceId] = newCreepName;
    }

    return newCreepName;
  }
}

/**
Locates an open position for a container that a creep can move on that
is adjacent to the given room's controller, marks it with a flag, and
creates a constructionSite for the container.
@param {Room} room
@param {RoomPosition} startPosition
*/
function placeUpgraderContainer(room, startPosition) {
  let steps = startPosition.findPathTo(room.controller.pos);
  let lastStep = steps[steps.length - 2];
  let flagName = room.name + " upContainer";

  room.createFlag(lastStep.x, lastStep.y, flagName, COLOR_PURPLE);
  Memory.flags[flagName] = Game.flags[flagName].pos;
  room.createConstructionSite(lastStep.x, lastStep.y, STRUCTURE_CONTAINER);
}

/**
Find an adjacent position for the given position and direction
@param {Room} room
@param {number} direction
*/
function findAdjacent(position, direction) {
  switch (direction) {
    case TOP:
      return new RoomPosition(position.x, position.y - 1, position.roomName);
    case TOP_RIGHT:
      return new RoomPosition(position.x + 1, position.y - 1, position.roomName);
    case RIGHT:
      return new RoomPosition(position.x + 1, position.y, position.roomName);
    case BOTTOM_RIGHT:
      return new RoomPosition(position.x + 1, position.y + 1, position.roomName);
    case BOTTOM:
      return new RoomPosition(position.x, position.y + 1, position.roomName);
    case BOTTOM_LEFT:
      return new RoomPosition(position.x - 1, position.y + 1, position.roomName);
    case LEFT:
      return new RoomPosition(position.x - 1, position.y, position.roomName);
    case TOP_LEFT:
      return new RoomPosition(position.x - 1, position.y - 1, position.roomName);
    default:
      return 0;
  }
}

/**
Finds the squares diagonal to the given position and returns them in an array.
Returned in order of TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT.
@param {RoomPosition} position
*/
function getCorners(position) {
  let corners = [];
  corners.push(findAdjacent(position, TOP_LEFT));
  corners.push(findAdjacent(position, TOP_RIGHT));
  corners.push(findAdjacent(position, BOTTOM_RIGHT));
  corners.push(findAdjacent(position, BOTTOM_LEFT));

  return corners;
}

/**
Returns the number of extensions and extension construction sites
in the room.
@param {Room} room
*/
function countExtensions(room) {
  let extensions = room.find(FIND_MY_STRUCTURES, {
    filter: {
      structureType: STRUCTURE_EXTENSION
    }
  });

  let extensionConstructionSites = room.find(FIND_MY_CONSTRUCTION_SITES, {
    filter: {
      structureType: STRUCTURE_EXTENSION
    }
  });

  return _.size(extensions.concat(extensionConstructionSites));
}

/**
Places construction sites for extensions on the squares diagonal to
the given position.
@param {Room} room
@param {RoomPosition} position
*/
function addExtensionsToDiagonals(room, position) {
  let corners = getCorners(position);

  console.log("Adding extensions at diagonals of location: " + JSON.stringify(position));

  //build on corners
  corners.forEach((corner) => {
    room.createConstructionSite(corner, STRUCTURE_EXTENSION);
  });

  return corners;
}

/**
Returns the concentric square layer to build new extensions in for the given
number of extensions.
@param {number} extensionCount
*/
function checkExtensionLayer(extensionCount) {
  let layer = 2;
  extensionCount -= Math.pow(layer, 2);

  while (extensionCount >= 0) {
    layer++;
    extensionCount -= (Math.pow(layer, 2) - Math.pow(layer - 2, 2));
  }
  return layer;
}

/**
Places construction sites in a line, starting at position, placing
numberOfConstructionSites sites of type structureType spaced dotLength apart.
dotLength is optional and defaults to 0.
Returns the position of the last constructionSite placed.
@param {RoomPosition} position
@param {number} direction
@param {number} structureType
@param {number} numberOfConstructionSites
@param {number} dotLength = 0
*/
function placeConstructionSitesInALine(position, direction, structureType,
  numberOfConstructionSites,
  dotLength = 0) {
  let positionOfLastConstructionSite = position;

  for (let i = numberOfConstructionSites; i > 0; i--) {
    Game.rooms[position.roomName].createConstructionSite(position, structureType);
    positionOfLastConstructionSite = position;
    for (let i = 0; i < dotLength + 1; i++) {
      position = findAdjacent(position, direction);
    }
  }

  return positionOfLastConstructionSite;
}

/**
Places extension construction sites around the given corners until the
maximum number of extensions have been placed in the given room.
@param {Room} room
*/
function addExtensionsToRoom(room, position) {
  let maxExtensions = CONTROLLER_STRUCTURES.extension[room.controller.level];

  //skip every other square so creeps have room to move.
  const DOT_LENGTH = 1;
  let extensionCount = countExtensions(room);

  if (extensionCount < maxExtensions) {
    let layer = checkExtensionLayer(extensionCount);

    //start by moving diagonally TOP_LEFT to the corner of the square.
    for (let i = layer - 1; i > 0; i--) {
      position = findAdjacent(position, TOP_LEFT);
    }

    //then place the sides of the square. Traces from top left, to top right,
    //to bottom right, to bottom left, finally back to top left.
    _.forEach([RIGHT, BOTTOM, LEFT, TOP], (direction) => {
      position = placeConstructionSitesInALine(position, direction, STRUCTURE_EXTENSION,
        layer,
        DOT_LENGTH);
    });
  }
}

module.exports = {
  checkForSources,
  findSourceIdMissingMiner,
  buildMiner,
  placeUpgraderContainer,
  findAdjacent,
  getCorners,
  countExtensions,
  addExtensionsToDiagonals,
  checkExtensionLayer,
  placeConstructionSitesInALine,
  addExtensionsToRoom
};
<commit_msg>Added function intended for placing towers near a target location, but making sure creeps can path to them. This function has other potential uses.<commit_after>let spawnFunctions = require('spawn');

/**
check for sources that are not known in this room and add
them to the list of possible sourceAssignments for this room.
@param {Room} room
*/
function checkForSources(room) {
  if (!room.memory.sourceAssignments) {
    room.memory.sourceAssignments = {};

    _.forEach(room.find(FIND_SOURCES), (source) => {
      if (!room.memory.sourceAssignments[source.id]) {
        room.memory.sourceAssignments[source.id] = "none";
      }
    });
  }
}

/**
Check if a source is missing a miner.
@param {Room} room
*/
function findSourceIdMissingMiner(room) {
  let result = 0;

  _.forEach(room.memory.sourceAssignments, (creepName, sourceId) => {
    if (!Game.creeps[creepName]) {
      result = sourceId;
    }
  });

  return result;
}

/**
Build a miner for the given room and sourceId.
@param {Room} room
@param {string} sourceId
@param {Spawn} spawn
@param {array} body = [WORK, WORK, CARRY, MOVE]
@param {String} name = undefined
*/
function buildMiner(room, sourceId, spawn, body = undefined, name = undefined) {
  if (sourceId) {
    let newCreepName = spawnFunctions.createCreepWithMemory(spawn, {
      role: "miner",
      assignedSourceId: sourceId
    }, body, name);

    if (newCreepName != ERR_NOT_ENOUGH_ENERGY && newCreepName != ERR_BUSY) {
      room.memory.sourceAssignments[sourceId] = newCreepName;
    }

    return newCreepName;
  }
}

/**
Locates an open position for a container that a creep can move on that
is adjacent to the given room's controller, marks it with a flag, and
creates a constructionSite for the container.
@param {Room} room
@param {RoomPosition} startPosition
*/
function placeUpgraderContainer(room, startPosition) {
  let steps = startPosition.findPathTo(room.controller.pos);
  let lastStep = steps[steps.length - 2];
  let flagName = room.name + " upContainer";

  room.createFlag(lastStep.x, lastStep.y, flagName, COLOR_PURPLE);
  Memory.flags[flagName] = Game.flags[flagName].pos;
  room.createConstructionSite(lastStep.x, lastStep.y, STRUCTURE_CONTAINER);
}

/**
Find an adjacent position for the given position and direction
@param {Room} room
@param {number} direction
*/
function findAdjacent(position, direction) {
  switch (direction) {
    case TOP:
      return new RoomPosition(position.x, position.y - 1, position.roomName);
    case TOP_RIGHT:
      return new RoomPosition(position.x + 1, position.y - 1, position.roomName);
    case RIGHT:
      return new RoomPosition(position.x + 1, position.y, position.roomName);
    case BOTTOM_RIGHT:
      return new RoomPosition(position.x + 1, position.y + 1, position.roomName);
    case BOTTOM:
      return new RoomPosition(position.x, position.y + 1, position.roomName);
    case BOTTOM_LEFT:
      return new RoomPosition(position.x - 1, position.y + 1, position.roomName);
    case LEFT:
      return new RoomPosition(position.x - 1, position.y, position.roomName);
    case TOP_LEFT:
      return new RoomPosition(position.x - 1, position.y - 1, position.roomName);
    default:
      return 0;
  }
}

/**
Finds the squares diagonal to the given position and returns them in an array.
Returned in order of TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT.
@param {RoomPosition} position
*/
function getCorners(position) {
  let corners = [];
  corners.push(findAdjacent(position, TOP_LEFT));
  corners.push(findAdjacent(position, TOP_RIGHT));
  corners.push(findAdjacent(position, BOTTOM_RIGHT));
  corners.push(findAdjacent(position, BOTTOM_LEFT));

  return corners;
}

/**
Returns the number of extensions and extension construction sites
in the room.
@param {Room} room
*/
function countExtensions(room) {
  let extensions = room.find(FIND_MY_STRUCTURES, {
    filter: {
      structureType: STRUCTURE_EXTENSION
    }
  });

  let extensionConstructionSites = room.find(FIND_MY_CONSTRUCTION_SITES, {
    filter: {
      structureType: STRUCTURE_EXTENSION
    }
  });

  return _.size(extensions.concat(extensionConstructionSites));
}

/**
Places construction sites for extensions on the squares diagonal to
the given position.
@param {Room} room
@param {RoomPosition} position
*/
function addExtensionsToDiagonals(room, position) {
  let corners = getCorners(position);

  console.log("Adding extensions at diagonals of location: " + JSON.stringify(position));

  //build on corners
  corners.forEach((corner) => {
    room.createConstructionSite(corner, STRUCTURE_EXTENSION);
  });

  return corners;
}

/**
Returns the concentric square layer to build new extensions in for the given
number of extensions.
@param {number} extensionCount
*/
function checkExtensionLayer(extensionCount) {
  let layer = 2;
  extensionCount -= Math.pow(layer, 2);

  while (extensionCount >= 0) {
    layer++;
    extensionCount -= (Math.pow(layer, 2) - Math.pow(layer - 2, 2));
  }
  return layer;
}

/**
Places construction sites in a line, starting at position, placing
numberOfConstructionSites sites of type structureType spaced dotLength apart.
dotLength is optional and defaults to 0.
Returns the position of the last constructionSite placed.
@param {RoomPosition} position
@param {number} direction
@param {number} structureType
@param {number} numberOfConstructionSites
@param {number} dotLength = 0
*/
function placeConstructionSitesInALine(position, direction, structureType,
  numberOfConstructionSites,
  dotLength = 0) {
  let positionOfLastConstructionSite = position;

  for (let i = numberOfConstructionSites; i > 0; i--) {
    Game.rooms[position.roomName].createConstructionSite(position, structureType);
    positionOfLastConstructionSite = position;
    for (let i = 0; i < dotLength + 1; i++) {
      position = findAdjacent(position, direction);
    }
  }

  return positionOfLastConstructionSite;
}

/**
Places extension construction sites around the given corners until the
maximum number of extensions have been placed in the given room.
@param {Room} room
*/
function addExtensionsToRoom(room, position) {
  let maxExtensions = CONTROLLER_STRUCTURES.extension[room.controller.level];

  //skip every other square so creeps have room to move.
  const DOT_LENGTH = 1;
  let extensionCount = countExtensions(room);

  if (extensionCount < maxExtensions) {
    let layer = checkExtensionLayer(extensionCount);

    //start by moving diagonally TOP_LEFT to the corner of the square.
    for (let i = layer - 1; i > 0; i--) {
      position = findAdjacent(position, TOP_LEFT);
    }

    //then place the sides of the square. Traces from top left, to top right,
    //to bottom right, to bottom left, finally back to top left.
    _.forEach([RIGHT, BOTTOM, LEFT, TOP], (direction) => {
      position = placeConstructionSitesInALine(position, direction, STRUCTURE_EXTENSION,
        layer,
        DOT_LENGTH);
    });
  }
}

/**
Places a construction site for the given structureType as close to the
endPosition as possible, while still placing it adjacent to (but not on) the
path between startPosition and endPosition.
@param {RoomPosition} startPosition
@param {RoomPosition} endPosition
@param {number} structureType
*/
function placeBuildingAdjacentToPathDestination(startPosition, endPosition, structureType) {
  let room = Game.rooms[startPosition.roomName];
  let path = startPosition.findPathTo(endPosition, {
    ignoreCreeps: true
  });

  if (path.length) {
    const FIRST_STEP = 0;
    const LAST_STEP = path.length - 1;
    for (let stepNumber = LAST_STEP - 1; stepNumber > FIRST_STEP; stepNumber--) {
      let step = path[stepNumber];
      let previousStep = path[stepNumber - 1];
      let nextStep = path[stepNumber + 1];

      let adjacentObjects = room.lookAtArea(step.y - 1, step.x - 1,
        step.y + 1, step.x + 1);

      //check each adjacent position to this position
      for (let yCoordinate in adjacentObjects) {
        yCoordinate = parseInt(yCoordinate);
        for (let xCoordinate in adjacentObjects[yCoordinate]) {
          xCoordinate = parseInt(xCoordinate);

          //if the position we are checking is not on the path, try to build here.
          if (!(xCoordinate == previousStep.x && yCoordinate == previousStep.y) &&
            !(xCoordinate == nextStep.x && yCoordinate == nextStep.y)) {

            let constructionSiteResult = room.createConstructionSite(xCoordinate, yCoordinate, structureType);
            if (constructionSiteResult == OK) {
              return OK;
            }
          }
        }
      }
    }
  }
}

module.exports = {
  checkForSources,
  findSourceIdMissingMiner,
  buildMiner,
  placeUpgraderContainer,
  findAdjacent,
  getCorners,
  countExtensions,
  addExtensionsToDiagonals,
  checkExtensionLayer,
  placeConstructionSitesInALine,
  addExtensionsToRoom,
  placeBuildingAdjacentToPathDestination
};
<|endoftext|>


<commit_before>import rules from './rules.js';
import attrs from './attrs.js';

const optionsDefault = {
	rules: rules,
	attrs: attrs
};

const clean = tree => tree
	.filter(node => {
		return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node)));
	})
	.map(node => {
		if (Object.prototype.hasOwnProperty.call(node, 'content')) {
			node.content = clean(node.content);
		}
		return typeof node === 'string' ? node.trim() : node;
	});

const indent = (tree, {rules: {indent, eol}}) => {
	const indentString = typeof indent === 'number' ? ' '.repeat(indent) : '\t';

	const getIndent = level => `${eol}${indentString.repeat(level)}`;

	const setIndent = (tree, level = 0) => tree.reduce((previousValue, currentValue, index) => {
		if (typeof currentValue === 'object' && Object.prototype.hasOwnProperty.call(currentValue, 'content')) {
			currentValue.content = setIndent(currentValue.content, ++level);
			--level;
		}

		if (tree.length === 1 && typeof tree[index] === 'string') {
			return [...previousValue, currentValue];
		}

		if (level === 0 && (tree.length - 1) === index) {
			return [...previousValue, currentValue];
		}

		if (level === 0) {
			return [...previousValue, currentValue, getIndent(level)];
		}

		if ((tree.length - 1) === index) {
			return [...previousValue, getIndent(level), currentValue, getIndent(--level)];
		}

		return [...previousValue, getIndent(level), currentValue, getIndent(0)];
	}, []);

	return setIndent(tree);
};

const attrsBoolean = (tree, {attrs: {boolean}}) => {
	const removeAttrValue = tree => tree.map(node => {
		if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) {
			node.content = removeAttrValue(node.content);
		}

		if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'attrs')) {
			Object.keys(node.attrs).forEach(key => {
				node.attrs[key] = boolean.includes(key) ? true : node.attrs[key];
			});
		}

		return node;
	});

	return removeAttrValue(tree);
};

function beautify(tree, options) {
	return new Promise(resolve => resolve(tree))
		.then(tree => clean(tree))
		.then(tree => indent(tree, options))
		.then(tree => attrsBoolean(tree, options))
		.then(tree => attrsBoolean(tree, options))
		.then(tree => tree);
}

export default (options = {}) => {
	return tree => new Promise((resolve, reject) => {
		if (!Array.isArray(tree)) {
			reject(new Error(`tree is not Array`));
		}

		if (tree.length === 0) {
			resolve(tree);
		}

		resolve(beautify(tree, Object.assign({}, optionsDefault, options)));
	});
};
<commit_msg>fix(index): fixed #15, incorrect set indent with posthtml-modules<commit_after>import parser from 'posthtml-parser';
import render from 'posthtml-render';
import rules from './rules.js';
import attrs from './attrs.js';

const optionsDefault = {
	rules: rules,
	attrs: attrs
};

const clean = tree => parser(render(tree))
	.filter(node => {
		return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node)));
	})
	.map(node => {
		if (Object.prototype.hasOwnProperty.call(node, 'content')) {
			node.content = clean(node.content);
		}

		return typeof node === 'string' ? node.trim() : node;
	});

const indent = (tree, {rules: {indent, eol}}) => {
	const indentString = typeof indent === 'number' ? ' '.repeat(indent) : '\t';

	const getIndent = level => `${eol}${indentString.repeat(level)}`;

	const setIndent = (tree, level = 0) => tree.reduce((previousValue, currentValue, index) => {
		if (typeof currentValue === 'object' && Object.prototype.hasOwnProperty.call(currentValue, 'content')) {
			currentValue.content = setIndent(currentValue.content, ++level);
			--level;
		}

		if (tree.length === 1 && typeof tree[index] === 'string') {
			return [...previousValue, currentValue];
		}

		if (level === 0 && (tree.length - 1) === index) {
			return [...previousValue, currentValue];
		}

		if (level === 0) {
			return [...previousValue, currentValue, getIndent(level)];
		}

		if ((tree.length - 1) === index) {
			return [...previousValue, getIndent(level), currentValue, getIndent(--level)];
		}

		return [...previousValue, getIndent(level), currentValue, getIndent(0)];
	}, []);

	return setIndent(tree);
};

const attrsBoolean = (tree, {attrs: {boolean}}) => {
	const removeAttrValue = tree => tree.map(node => {
		if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'content')) {
			node.content = removeAttrValue(node.content);
		}

		if (typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'attrs')) {
			Object.keys(node.attrs).forEach(key => {
				node.attrs[key] = boolean.includes(key) ? true : node.attrs[key];
			});
		}

		return node;
	});

	return removeAttrValue(tree);
};

function beautify(tree, options) {
	return Promise.resolve(tree)
		.then(tree => clean(tree))
		.then(tree => indent(tree, options))
		.then(tree => attrsBoolean(tree, options))
		.then(tree => attrsBoolean(tree, options))
		.then(tree => tree);
}

export default (options = {}) => {
	return tree => new Promise((resolve, reject) => {
		if (!Array.isArray(tree)) {
			reject(new Error(`tree is not Array`));
		}

		if (tree.length === 0) {
			resolve(tree);
		}

		resolve(beautify(tree, Object.assign({}, optionsDefault, options)));
	});
};
<|endoftext|>