<commit_before>'use strict';

const _ = require('lodash');
const express = require('express');
const wizard = require('hof-form-wizard');
const translate = require('i18n-future').middleware;
const deepTranslate = require('hof-middleware').deepTranslate;
const expressPartialTemplates = require('express-partial-templates');
const helpers = require('./helpers');
const deprecate = require('deprecate');

function applyBehaviours(steps, behaviours) {
  _.each(steps, (step) => {
    step.behaviours = [].concat(behaviours).concat(step.behaviours).filter(a => a);
  });
}

function getWizardConfig(config) {
  const wizardConfig = {
    name: config.route.name || (config.route.baseUrl || '').replace('/', '')
  };

  if (config.appConfig) {
    wizardConfig.appConfig = config.appConfig;
  }

  // whitelist properties from the route's config that should be passed into the form wizard
  const props = [
    'confirmStep',
    'params'
  ];
  Object.assign(wizardConfig, _.pick(config.route, props));

  return wizardConfig;
}

const returnBaseUrl = (url = '') => {
  let splitUrl = url.split('/');

  return splitUrl.length > 2 ? `/${splitUrl[1]}` : '/';
};

module.exports = config => {

  const app = express();
  const paths = helpers.getPaths(config);
  const fields = helpers.getFields(paths.fields);
  const routeViews = helpers.getViews(paths.views, config.route.views);
  const sharedViews = config.sharedViews;

  const views = routeViews ? [routeViews].concat(sharedViews) : sharedViews;

  app.set('x-powered-by', false);
  app.set('view engine', config.viewEngine);
  app.set('views', views);

  app.use(expressPartialTemplates(app));

  app.use(translate({
    resources: config.theme.translations,
    path: paths.translations + '/__lng__/__ns__.json'
  }));
  app.use(deepTranslate());

  const wizardConfig = getWizardConfig(config);

  if (config.baseController) {
    deprecate(
      '`baseController` option is deprecated and may be removed in future versions.',
      'Use `behaviours` option to define global behaviours.'
    );
    wizardConfig.controller = config.baseController;
  }

  if (config.route.behaviours) {
    applyBehaviours(config.route.steps, config.route.behaviours);
  }

  if (config.behaviours) {
    applyBehaviours(config.route.steps, config.behaviours);
  }

  Object.keys(config.route.pages || {}).forEach(path => {
    app.get(path, (req, res) => {
      let objPath = config.route.pages[path];

      if (typeof objPath === 'string') {
        objPath = {
          template: objPath,
          title: objPath
        };
      }
      res.locals = Object.assign(res.locals, {
        baseUrl: returnBaseUrl(req.originalUrl),
        refererUrl: returnBaseUrl(req.headers.referer),
        title: objPath.title
      });
      return res.render(objPath.template);
    });
  });

  if (config.route.steps) {
    app.use(wizard(config.route.steps, fields, wizardConfig));
  }

  return app;

};
<commit_msg>SA-2329; fixes regression toi refererURL local variable<commit_after>'use strict';

const _ = require('lodash');
const express = require('express');
const wizard = require('hof-form-wizard');
const translate = require('i18n-future').middleware;
const deepTranslate = require('hof-middleware').deepTranslate;
const expressPartialTemplates = require('express-partial-templates');
const helpers = require('./helpers');
const deprecate = require('deprecate');

function applyBehaviours(steps, behaviours) {
  _.each(steps, (step) => {
    step.behaviours = [].concat(behaviours).concat(step.behaviours).filter(a => a);
  });
}

function getWizardConfig(config) {
  const wizardConfig = {
    name: config.route.name || (config.route.baseUrl || '').replace('/', '')
  };

  if (config.appConfig) {
    wizardConfig.appConfig = config.appConfig;
  }

  // whitelist properties from the route's config that should be passed into the form wizard
  const props = [
    'confirmStep',
    'params'
  ];
  Object.assign(wizardConfig, _.pick(config.route, props));

  return wizardConfig;
}

const returnBaseUrl = (url = '') => {
  let splitUrl = url.split('/');

  return splitUrl.length > 2 ? `/${splitUrl[1]}` : '/';
};

module.exports = config => {

  const app = express();
  const paths = helpers.getPaths(config);
  const fields = helpers.getFields(paths.fields);
  const routeViews = helpers.getViews(paths.views, config.route.views);
  const sharedViews = config.sharedViews;

  const views = routeViews ? [routeViews].concat(sharedViews) : sharedViews;

  app.set('x-powered-by', false);
  app.set('view engine', config.viewEngine);
  app.set('views', views);

  app.use(expressPartialTemplates(app));

  app.use(translate({
    resources: config.theme.translations,
    path: paths.translations + '/__lng__/__ns__.json'
  }));
  app.use(deepTranslate());

  const wizardConfig = getWizardConfig(config);

  if (config.baseController) {
    deprecate(
      '`baseController` option is deprecated and may be removed in future versions.',
      'Use `behaviours` option to define global behaviours.'
    );
    wizardConfig.controller = config.baseController;
  }

  if (config.route.behaviours) {
    applyBehaviours(config.route.steps, config.route.behaviours);
  }

  if (config.behaviours) {
    applyBehaviours(config.route.steps, config.behaviours);
  }

  Object.keys(config.route.pages || {}).forEach(path => {
    app.get(path, (req, res) => {
      let objPath = config.route.pages[path];

      if (typeof objPath === 'string') {
        objPath = {
          template: objPath,
          title: objPath
        };
      }
      res.locals = Object.assign(res.locals, {
        baseUrl: returnBaseUrl(req.originalUrl),
        refererUrl: req.headers.referer,
        title: objPath.title
      });
      return res.render(objPath.template);
    });
  });

  if (config.route.steps) {
    app.use(wizard(config.route.steps, fields, wizardConfig));
  }

  return app;

};
<|endoftext|>


<commit_before>var mongodb = process.env['TEST_NATIVE'] != null ? require('../../lib/mongodb').native() : require('../../lib/mongodb').pure();
var noReplicasetStart = process.env['NO_REPLICASET_START'] != null ? true : false;

var testCase = require('nodeunit').testCase,
  debug = require('util').debug,
  inspect = require('util').inspect,
  gleak = require('../../dev/tools/gleak'),
  ShardedManager = require('../tools/sharded_manager').ShardedManager,
  Db = mongodb.Db,
  Mongos = mongodb.Mongos,
  ReadPreference = mongodb.ReadPreference,
  MongoClient = mongodb.MongoClient,
  Server = mongodb.Server;

// Keep instance of ReplicaSetManager
var serversUp = false;
var retries = 120;
var Shard = Shard == null ? null : Shard;

/**
 * Retrieve the server information for the current
 * instance of the db client
 *
 * @ignore
 */
exports.setUp = function(callback) {
  Shard = new ShardedManager({
    // A single replicaset in our sharded system
    numberOfReplicaSets:1,
    replPortRangeSet:30000,
    // A single configuration server
    numberOfConfigServers:1,
    configPortRangeSet:40000,
    // Two mongos proxies to ensure correct failover
    numberOfMongosServers:2,
    mongosRangeSet:50000,
    // Collection and shard key setup
    db:"sharded_test_db",
    collection:"sharded_test_db_collection",
    shardKey: "_id"
  })

  // Start the shard
  Shard.start(function(err, result) {
    callback();
  });
}

/**
 * Retrieve the server information for the current
 * instance of the db client
 *
 * @ignore
 */
exports.tearDown = function(callback) {
  // Shard.killAll(function() {
    callback();
  // });
}

/**
 * Example of a simple url connection string to a shard, with acknowledgement of writes.
 *
 * @_class mongoclient
 * @_function MongoClient.connect
 */
exports['Should connect to mongos proxies using connectiong string'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001/sharded_test_db?w=1', function(err, db) {
    test.equal(null, err);
    test.ok(db != null);

    db.collection("replicaset_mongo_client_collection").update({a:1}, {b:1}, {upsert:true}, function(err, result) {
      test.equal(null, err);
      test.equal(1, result);

      db.close();
      test.done();
    });
  });
}

/**
 * @ignore
 */
exports['Should connect to mongos proxies using connectiong string and options'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001/sharded_test_db?w=1', {
    mongos: {
      haInterval: 500
    }
  }, function(err, db) {
    test.equal(null, err);
    test.ok(db != null);
    test.equal(500, db.serverConfig.mongosStatusCheckInterval);

    db.collection("replicaset_mongo_client_collection").update({a:1}, {b:1}, {upsert:true}, function(err, result) {
      test.equal(null, err);
      test.equal(1, result);

      db.close();
      test.done();
    });
  });
}

/**
 * @ignore
 */
exports['Should correctly connect and then handle a mongos failure'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001/sharded_test_db?w=1', {
    // mongos: {
    //   haInterval: 500
    // }
  }, function(err, db) {
    test.equal(null, err);
    test.ok(db != null);
    // test.equal(500, db.serverConfig.mongosStatusCheckInterval);

    db.collection("replicaset_mongo_client_collection").update({a:1}, {b:1}, {upsert:true}, function(err, result) {
      test.equal(null, err);
      test.equal(1, result);
      var numberOfTicks = 10;

      var ticker = function() {
        numberOfTicks = numberOfTicks - 1;

        db.collection('replicaset_mongo_client_collection').findOne(function(err, doc) {
          if(numberOfTicks == 0) {
            db.close();
            test.done();
          } else {
            setTimeout(ticker, 1000);
          }
        });
      }

      var killport = db.serverConfig._currentMongos.port;

      // Kill the mongos proxy
      Shard.killMongoS(killport, function(err, result) {
        setTimeout(ticker, 1000);
      });
    });
  });
}

/**
 * Retrieve the server information for the current
 * instance of the db client
 *
 * @ignore
 */
var numberOfTestsRun = Object.keys(this).length - 2;
<commit_msg>Added test for issue #885, passes with master<commit_after>var mongodb = process.env['TEST_NATIVE'] != null ? require('../../lib/mongodb').native() : require('../../lib/mongodb').pure();
var noReplicasetStart = process.env['NO_REPLICASET_START'] != null ? true : false;

var testCase = require('nodeunit').testCase,
  debug = require('util').debug,
  inspect = require('util').inspect,
  gleak = require('../../dev/tools/gleak'),
  ShardedManager = require('../tools/sharded_manager').ShardedManager,
  Db = mongodb.Db,
  Mongos = mongodb.Mongos,
  ReadPreference = mongodb.ReadPreference,
  MongoClient = mongodb.MongoClient,
  Server = mongodb.Server;

// Keep instance of ReplicaSetManager
var serversUp = false;
var retries = 120;
var Shard = Shard == null ? null : Shard;

/**
 * Retrieve the server information for the current
 * instance of the db client
 *
 * @ignore
 */
exports.setUp = function(callback) {
  Shard = new ShardedManager({
    // A single replicaset in our sharded system
    numberOfReplicaSets:1,
    replPortRangeSet:30000,
    // A single configuration server
    numberOfConfigServers:1,
    configPortRangeSet:40000,
    // Two mongos proxies to ensure correct failover
    numberOfMongosServers:2,
    mongosRangeSet:50000,
    // Collection and shard key setup
    db:"sharded_test_db",
    collection:"sharded_test_db_collection",
    shardKey: "_id"
  })

  // Start the shard
  Shard.start(function(err, result) {
    callback();
  });
}

/**
 * Retrieve the server information for the current
 * instance of the db client
 *
 * @ignore
 */
exports.tearDown = function(callback) {
  // Shard.killAll(function() {
    callback();
  // });
}

/**
 * Example of a simple url connection string to a shard, with acknowledgement of writes.
 *
 * @_class mongoclient
 * @_function MongoClient.connect
 */
exports['Should connect to mongos proxies using connectiong string'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001/sharded_test_db?w=1', function(err, db) {
    test.equal(null, err);
    test.ok(db != null);

    db.collection("replicaset_mongo_client_collection").update({a:1}, {b:1}, {upsert:true}, function(err, result) {
      test.equal(null, err);
      test.equal(1, result);

      db.close();
      test.done();
    });
  });
}

/**
 * @ignore
 */
exports['Should connect to mongos proxies using connectiong string and options'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001/sharded_test_db?w=1', {
    mongos: {
      haInterval: 500
    }
  }, function(err, db) {
    test.equal(null, err);
    test.ok(db != null);
    test.equal(500, db.serverConfig.mongosStatusCheckInterval);

    db.collection("replicaset_mongo_client_collection").update({a:1}, {b:1}, {upsert:true}, function(err, result) {
      test.equal(null, err);
      test.equal(1, result);

      db.close();
      test.done();
    });
  });
}

/**
 * @ignore
 */
exports['Should correctly connect and then handle a mongos failure'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001/sharded_test_db?w=1', {
    // mongos: {
    //   haInterval: 500
    // }
  }, function(err, db) {
    test.equal(null, err);
    test.ok(db != null);
    // test.equal(500, db.serverConfig.mongosStatusCheckInterval);

    db.collection("replicaset_mongo_client_collection").update({a:1}, {b:1}, {upsert:true}, function(err, result) {
      test.equal(null, err);
      test.equal(1, result);
      var numberOfTicks = 10;

      var ticker = function() {
        numberOfTicks = numberOfTicks - 1;

        db.collection('replicaset_mongo_client_collection').findOne(function(err, doc) {
          if(numberOfTicks == 0) {
            db.close();
            test.done();
          } else {
            setTimeout(ticker, 1000);
          }
        });
      }

      var killport = db.serverConfig._currentMongos.port;

      // Kill the mongos proxy
      Shard.killMongoS(killport, function(err, result) {
        setTimeout(ticker, 1000);
      });
    });
  });
}

/**
 * @ignore
 */
exports['Should correctly connect with a missing mongos'] = function(test) {
  MongoClient.connect('mongodb://localhost:50000,localhost:50001,localhost:50002/test', {}, function(err, db) {
    setTimeout(function() {
      test.equal(null, err);
      test.ok(db != null);
      db.close();
      test.done();
    }, 10000)
  });
}

/**
 * Retrieve the server information for the current
 * instance of the db client
 *
 * @ignore
 */
var numberOfTestsRun = Object.keys(this).length - 2;
<|endoftext|>


<commit_before>"use strict";

let baxel_router = require("baxel-route")
  , c = require("color-console")
  , config = require("./config")
  , vhost = require("koa-vhost")
  , fs = require("fs")
  , session = null
  , middleware = require("./middleware");

if (fs.existsSync(process.cwd() + "/node_modules/koa-generic-session")) {
  session = require(process.cwd() + "/node_modules/koa-generic-session");
}

module.exports = function(app, koa) {

  c.green("\tRoutes:");

  if (!process.env.HOST) {
    c.red("please set environment variable HOST in .env file");
  } else {
    for (let domain in config.routes) {
      let router = baxel_router(config.routes[domain], {
        controller_path: process.env.CONTROLLER_PATH,
        helper_path: process.env.HELPER_PATH
      });

      let subapp = koa()
        , host = (domain !== "root" ? domain + "." : "") + process.env.HOST;

      c.green("\t\t" + host);

      // Apply middleware if exists
      middleware(app, subapp, domain);

      subapp
        .use(router.routes())
        .use(router.allowedMethods());

      app.use(vhost({
        host: host,
        app: subapp
      }));

      for (var i in router._routes) {
        c.green("\t\t\t" + router._routes[i]);
      }
    }
  }
};<commit_msg>Routes now returns the routes<commit_after>"use strict";

let baxel_router = require("baxel-route")
  , c = require("color-console")
  , config = require("./config")
  , vhost = require("koa-vhost")
  , fs = require("fs")
  , session = null
  , middleware = require("./middleware");

if (fs.existsSync(process.cwd() + "/node_modules/koa-generic-session")) {
  session = require(process.cwd() + "/node_modules/koa-generic-session");
}

module.exports = function(app, koa) {

  c.green("\tRoutes:");

  if (!process.env.HOST) {
    c.red("please set environment variable HOST in .env file");
  } else {
    let routes = {};
    for (let domain in config.routes) {
      let router = baxel_router(config.routes[domain], {
        controller_path: process.env.CONTROLLER_PATH,
        helper_path: process.env.HELPER_PATH
      });

      let subapp = koa()
        , host = (domain !== "root" ? domain + "." : "") + process.env.HOST;

      c.green("\t\t" + host);

      // Apply middleware if exists
      middleware(app, subapp, domain);

      subapp
        .use(router.routes())
        .use(router.allowedMethods());

      app.use(vhost({
        host: host,
        app: subapp
      }));

      routes[host] = router._routes;

      for (var i in router._routes) {
        c.green("\t\t\t" + router._routes[i]);
      }
    }

    return routes;
  }

  return null;
};<|endoftext|>


<commit_before>import { parse } from 'url'
import evalScript from './eval-script'
import shallowEquals from './shallow-equals'

export default class Router {
  constructor (url, initialData) {
    const parsed = parse(url, true)

    // represents the current component key
    this.route = toRoute(parsed.pathname)

    // set up the component cache (by route keys)
    this.components = { [this.route]: initialData }

    this.pathname = parsed.pathname
    this.query = parsed.query
    this.subscriptions = new Set()
    this.componentLoadCancel = null
    this.onPopState = this.onPopState.bind(this)

    if (typeof window !== 'undefined') {
      window.addEventListener('popstate', this.onPopState)
    }
  }

  onPopState (e) {
    this.abortComponentLoad()

    const { pathname, query } = parse(window.location.href, true)
    const route = (e.state || {}).route || toRoute(pathname)

    Promise.resolve()
    .then(async () => {
      const data = await this.fetchComponent(route)
      let props
      if (route !== this.route) {
        const ctx = { ...data.ctx, pathname, query }
        props = await this.getInitialProps(data.Component, ctx)
      }

      this.route = route
      this.set(getURL(), { ...data, props })
    })
    .catch((err) => {
      if (err.cancelled) return

      // the only way we can appropriately handle
      // this failure is deferring to the browser
      // since the URL has already changed
      window.location.reload()
    })
  }

  update (route, Component) {
    const data = this.components[route] || {}
    const newData = { ...data, Component }
    this.components[route] = newData

    if (route === this.route) {
      this.notify(newData)
    }
  }

  async reload (route) {
    delete this.components[route]

    if (route !== this.route) return

    const { pathname, query } = parse(window.location.href, true)

    let data
    let props
    try {
      data = await this.fetchComponent(route)
      if (route === this.route) {
        const ctx = { ...data.ctx, pathname, query }
        props = await this.getInitialProps(data.Component, ctx)
      }
    } catch (err) {
      if (err.cancelled) return false
      throw err
    }

    this.notify({ ...data, props })
  }

  back () {
    window.history.back()
  }

  push (route, url) {
    return this.change('pushState', route, url)
  }

  replace (route, url) {
    return this.change('replaceState', route, url)
  }

  async change (method, route, url) {
    const { pathname, query } = parse(url, true)

    if (!route) route = toRoute(pathname)

    this.abortComponentLoad()

    let data
    let props
    try {
      data = await this.fetchComponent(route)
      if (route !== this.route) {
        const ctx = { ...data.ctx, pathname, query }
        props = await this.getInitialProps(data.Component, ctx)
      }
    } catch (err) {
      if (err.cancelled) return false
      throw err
    }

    if (getURL() !== url) {
      window.history[method]({ route }, null, url)
    }

    this.route = route
    this.set(url, { ...data, props })
    return true
  }

  set (url, data) {
    const parsed = parse(url, true)

    if (this.urlIsNew(parsed)) {
      this.pathname = parsed.pathname
      this.query = parsed.query
      this.notify(data)
    }
  }

  urlIsNew ({ pathname, query }) {
    return this.pathname !== pathname || !shallowEquals(query, this.query)
  }

  async fetchComponent (url) {
    const route = toRoute(parse(url).pathname)

    let data = this.components[route]
    if (!data) {
      let cancel

      const componentUrl = toJSONUrl(route)
      data = await new Promise((resolve, reject) => {
        this.componentLoadCancel = cancel = () => {
          if (xhr.abort) xhr.abort()
        }

        const xhr = loadComponent(componentUrl, (err, data) => {
          if (err) return reject(err)
          resolve({
            Component: data.Component,
            ctx: { xhr, err: data.err }
          })
        })
      })

      if (cancel === this.componentLoadCancel) {
        this.componentLoadCancel = null
      }

      this.components[route] = data
    }
    return data
  }

  async getInitialProps (Component, ctx) {
    let cancelled = false
    const cancel = () => { cancelled = true }
    this.componentLoadCancel = cancel

    const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})

    if (cancel === this.componentLoadCancel) {
      this.componentLoadCancel = null
    }

    if (cancelled) {
      const err = new Error('Cancelled')
      err.cancelled = true
      throw err
    }

    return props
  }

  abortComponentLoad () {
    if (this.componentLoadCancel) {
      this.componentLoadCancel()
      this.componentLoadCancel = null
    }
  }

  notify (data) {
    this.subscriptions.forEach((fn) => fn(data))
  }

  subscribe (fn) {
    this.subscriptions.add(fn)
    return () => this.subscriptions.delete(fn)
  }
}

function getURL () {
  return window.location.pathname + (window.location.search || '') + (window.location.hash || '')
}

function toRoute (path) {
  return path.replace(/\/$/, '') || '/'
}

function toJSONUrl (route) {
  return (route === '/' ? '/index' : route) + '.json'
}

function loadComponent (url, fn) {
  return loadJSON(url, (err, data) => {
    if (err) return fn(err)

    let module
    try {
      module = evalScript(data.component)
    } catch (err) {
      return fn(err)
    }

    const Component = module.default || module
    fn(null, { Component, err: data.err })
  })
}

function loadJSON (url, fn) {
  const xhr = new window.XMLHttpRequest()
  xhr.onload = () => {
    let data

    try {
      data = JSON.parse(xhr.responseText)
    } catch (err) {
      fn(new Error('Failed to load JSON for ' + url))
      return
    }

    fn(null, data)
  }
  xhr.onerror = () => {
    fn(new Error('XHR failed. Status: ' + xhr.status))
  }
  xhr.onabort = () => {
    const err = new Error('XHR aborted')
    err.cancelled = true
    fn(err)
  }
  xhr.open('GET', url)
  xhr.send()

  return xhr
}
<commit_msg>fix getInitialProps doesn't get called when only url query was changed<commit_after>import { parse } from 'url'
import evalScript from './eval-script'
import shallowEquals from './shallow-equals'

export default class Router {
  constructor (url, initialData) {
    const parsed = parse(url, true)

    // represents the current component key
    this.route = toRoute(parsed.pathname)

    // set up the component cache (by route keys)
    this.components = { [this.route]: initialData }

    this.pathname = parsed.pathname
    this.query = parsed.query
    this.subscriptions = new Set()
    this.componentLoadCancel = null
    this.onPopState = this.onPopState.bind(this)

    if (typeof window !== 'undefined') {
      window.addEventListener('popstate', this.onPopState)
    }
  }

  onPopState (e) {
    this.abortComponentLoad()

    const { pathname, query } = parse(window.location.href, true)
    const route = (e.state || {}).route || toRoute(pathname)

    Promise.resolve()
    .then(async () => {
      const data = await this.fetchComponent(route)
      const ctx = { ...data.ctx, pathname, query }
      const props = await this.getInitialProps(data.Component, ctx)

      this.route = route
      this.set(getURL(), { ...data, props })
    })
    .catch((err) => {
      if (err.cancelled) return

      // the only way we can appropriately handle
      // this failure is deferring to the browser
      // since the URL has already changed
      window.location.reload()
    })
  }

  update (route, Component) {
    const data = this.components[route] || {}
    const newData = { ...data, Component }
    this.components[route] = newData

    if (route === this.route) {
      this.notify(newData)
    }
  }

  async reload (route) {
    delete this.components[route]

    if (route !== this.route) return

    const { pathname, query } = parse(window.location.href, true)

    let data
    let props
    try {
      data = await this.fetchComponent(route)
      const ctx = { ...data.ctx, pathname, query }
      props = await this.getInitialProps(data.Component, ctx)
    } catch (err) {
      if (err.cancelled) return false
      throw err
    }

    this.notify({ ...data, props })
  }

  back () {
    window.history.back()
  }

  push (route, url) {
    return this.change('pushState', route, url)
  }

  replace (route, url) {
    return this.change('replaceState', route, url)
  }

  async change (method, route, url) {
    const { pathname, query } = parse(url, true)

    if (!route) route = toRoute(pathname)

    this.abortComponentLoad()

    let data
    let props
    try {
      data = await this.fetchComponent(route)
      const ctx = { ...data.ctx, pathname, query }
      props = await this.getInitialProps(data.Component, ctx)
    } catch (err) {
      if (err.cancelled) return false
      throw err
    }

    if (getURL() !== url) {
      window.history[method]({ route }, null, url)
    }

    this.route = route
    this.set(url, { ...data, props })
    return true
  }

  set (url, data) {
    const parsed = parse(url, true)

    if (this.urlIsNew(parsed)) {
      this.pathname = parsed.pathname
      this.query = parsed.query
      this.notify(data)
    }
  }

  urlIsNew ({ pathname, query }) {
    return this.pathname !== pathname || !shallowEquals(query, this.query)
  }

  async fetchComponent (url) {
    const route = toRoute(parse(url).pathname)

    let data = this.components[route]
    if (!data) {
      let cancel

      const componentUrl = toJSONUrl(route)
      data = await new Promise((resolve, reject) => {
        this.componentLoadCancel = cancel = () => {
          if (xhr.abort) xhr.abort()
        }

        const xhr = loadComponent(componentUrl, (err, data) => {
          if (err) return reject(err)
          resolve({
            Component: data.Component,
            ctx: { xhr, err: data.err }
          })
        })
      })

      if (cancel === this.componentLoadCancel) {
        this.componentLoadCancel = null
      }

      this.components[route] = data
    }
    return data
  }

  async getInitialProps (Component, ctx) {
    let cancelled = false
    const cancel = () => { cancelled = true }
    this.componentLoadCancel = cancel

    const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})

    if (cancel === this.componentLoadCancel) {
      this.componentLoadCancel = null
    }

    if (cancelled) {
      const err = new Error('Cancelled')
      err.cancelled = true
      throw err
    }

    return props
  }

  abortComponentLoad () {
    if (this.componentLoadCancel) {
      this.componentLoadCancel()
      this.componentLoadCancel = null
    }
  }

  notify (data) {
    this.subscriptions.forEach((fn) => fn(data))
  }

  subscribe (fn) {
    this.subscriptions.add(fn)
    return () => this.subscriptions.delete(fn)
  }
}

function getURL () {
  return window.location.pathname + (window.location.search || '') + (window.location.hash || '')
}

function toRoute (path) {
  return path.replace(/\/$/, '') || '/'
}

function toJSONUrl (route) {
  return (route === '/' ? '/index' : route) + '.json'
}

function loadComponent (url, fn) {
  return loadJSON(url, (err, data) => {
    if (err) return fn(err)

    let module
    try {
      module = evalScript(data.component)
    } catch (err) {
      return fn(err)
    }

    const Component = module.default || module
    fn(null, { Component, err: data.err })
  })
}

function loadJSON (url, fn) {
  const xhr = new window.XMLHttpRequest()
  xhr.onload = () => {
    let data

    try {
      data = JSON.parse(xhr.responseText)
    } catch (err) {
      fn(new Error('Failed to load JSON for ' + url))
      return
    }

    fn(null, data)
  }
  xhr.onerror = () => {
    fn(new Error('XHR failed. Status: ' + xhr.status))
  }
  xhr.onabort = () => {
    const err = new Error('XHR aborted')
    err.cancelled = true
    fn(err)
  }
  xhr.open('GET', url)
  xhr.send()

  return xhr
}
<|endoftext|>


<commit_before>// SpookyJS provides our bridge to CasperJS and PhantomJS
var Spooky = require('spooky');

var file = require('./file.js')
  , dl = require('./download.js')
  , url = require('./url.js')
  , dom = require('./dom.js')
  , Ticker = require('./ticker.js');

var scrape = module.exports;

// Bubble SpookyJS errors up to our interface,
// providing a clear context message and the SpookyJS message
// as detail.
//
// @param {String} err the SpookyJS error.
var handleInitError = function(err) {
  if (err) {
    var e = new Error('Failed to initialize SpookyJS');
    e.details = err;
    log.error(e);
    log.debug(e.stack);
  }
};

// generate SpookyJS settings
// @param {String} loglevel the loglevel
// @return {Object} the settings
var settings = function(loglevel) {
  return {
    child: {
      transport: 'http'
    },
    casper: {
      logLevel: loglevel,
      verbose: true,
      exitOnError: true,
      pageSettings: {
        loadImages: false,
        loadPlugins: false
      },
      resourceTimeout: 20000,
      onResourceTimeout: function(e) {
        console.log('page load timeout - has the connection dropped?');
        console.log(e.errorCode);   // it'll probably be 408
        console.log(e.errorString); // it'll probably be 'Network timeout on resource'
        console.log(e.url);         // the url whose request timed out
        casper.exit(1);
      }
    }
  };
}

// Scrape a URL using a ScraperJSON-defined scraper.
//
// @param {String} scrapeUrl the URL to scrape
// @param {Object} definition a dictionary defining the scraper
// @param {Function} cb callback to run once this scrape has finished
// @return {Object} a dictionary containing the scraping results
scrape.scrape = function(scrapeUrl, definition, cb, loglevel) {

  // validate arguments
  url.checkUrl(scrapeUrl);

  // let's get our scrape on
  var spooky = new Spooky(settings(loglevel), function() {
    spooky.start(scrapeUrl);

    spooky.then(function() {
      // in SpookyJS scope
      this.emit('pagedownload', this.evaluate(function() {
        // in rendered page scope
        return document.all[0].outerHTML;
      }));

    });

    spooky.run();
  });

  spooky.on('pagedownload', function(html) {
    log.debug('page downloaded and rendered');
    try {
      scrape.scrapeHtml(html, definition, scrapeUrl, cb);
    } catch(e) {
      log.error('problem scraping html:');
      log.error(e.message);
      log.error(e.stack);
    }
  });

  if (loglevel == 'debug') {
    spooky.on('console', function (line) {
      var parts = line.split(' ');
      log.debug(parts.slice(1, parts.length).join(' '));
    });

    spooky.on('log', function (log) {
      if (log.space === 'remote') {
        console.log(log.message.replace(/ \- .*/, ''));
      }
    });
  }

};

// Parse the rendered HTML using the scraper definition.
// Downloadable resources are saved to disk, and the successfully
// parsed elements are output as JSON.
//
// @param {String} html the HTML source of the rendered page
// @param {Object} definition the scraper definition
scrape.scrapeHtml = function(html, definition, scrapeUrl, cb) {
  var ticker = new Ticker(2, cb);

  // save the rendered html
  log.debug('saving rendered HTML');
  file.write('rendered.html', html, function(){ ticker.tick() });

  // load HTML into DOM
  log.debug('scraping rendered DOM');
  var doc = dom.render(html);

  // scrape the DOM using the ScraperJSON scraper
  var results = scrape.scrapeScraperJSON(definition,
                                         doc,
                                         scrapeUrl,
                                         cb,
                                         ticker);

  // save results JSON
  var json = JSON.stringify(results, undefined, 2);
  file.write('results.json', json, function(){ ticker.tick() });
}

scrape.scrapeScraperJSON =
  function(definition, doc, scrapeUrl, cb, ticker) {
  if (!ticker) {
    ticker = new Ticker(0, cb);
  }
  var results = [];

  // we don't know how many downloads will be spawned by each
  // element, so remove the Node.js cap of 10 listeners per
  // emitter (0 == infinty)
  process.setMaxListeners(0);

  for (var key in definition) {
    try {
      var element = definition[key];
      log.debug('scraping element:', key);
      // extract element
      var selector = element.selector;
      var attribute = element.attribute;
      var matches = dom.select(selector, doc);
      log.debug("found", matches.length, "matches");
      for (var i = 0; i < matches.length; i++) {
        var res = matches[i];
        if (res) {
          res = dom.getAttribute(res, attribute);
          log.data(key + ":", dom.cleanElement(res));

          // save the result
          var data = {};
          data[key] = res;
          results.push(data);

          // process downloads
          if (element.download) {
            // set download running
            var down = dl.downloadResource(res, scrapeUrl);
            // add it to the task ticker
            ticker.elongate();
            down.on('close', function() {
              log.debug('download done');
              ticker.tick();
            });
            down.on('error', function(err) {
              log.error('file download failed: ' + err);
              ticker.tick();
            });
          }
        } else {
          log.warn('could not capture element');
        }
      }
    } catch(err) {
      // errors deep in the stack should be presented, but
      // we want to continue with the rest of the execution
      log.error('problem applying scraperJSON scraper:');
      log.error(err.message);
      log.error(err.stack);
    }
  }
  if (results.length == 0) {
    log.warn('no elements were extracted from url:', scrapeUrl);
  }
  if (ticker.length > 2) {
    log.info('waiting for',
             ticker.length - 2,
             'downloads to complete in background');
  }
  return results;
}
<commit_msg>log error if html not rendered<commit_after>// SpookyJS provides our bridge to CasperJS and PhantomJS
var Spooky = require('spooky');

var file = require('./file.js')
  , dl = require('./download.js')
  , url = require('./url.js')
  , dom = require('./dom.js')
  , Ticker = require('./ticker.js');

var scrape = module.exports;

// Bubble SpookyJS errors up to our interface,
// providing a clear context message and the SpookyJS message
// as detail.
//
// @param {String} err the SpookyJS error.
var handleInitError = function(err) {
  if (err) {
    var e = new Error('Failed to initialize SpookyJS');
    e.details = err;
    log.error(e);
    log.debug(e.stack);
  }
};

// generate SpookyJS settings
// @param {String} loglevel the loglevel
// @return {Object} the settings
var settings = function(loglevel) {
  return {
    child: {
      transport: 'http'
    },
    casper: {
      logLevel: loglevel,
      verbose: true,
      exitOnError: true,
      pageSettings: {
        loadImages: false,
        loadPlugins: false
      },
      resourceTimeout: 20000,
      onResourceTimeout: function(e) {
        console.log('page load timeout - has the connection dropped?');
        console.log(e.errorCode);   // it'll probably be 408
        console.log(e.errorString); // it'll probably be 'Network timeout on resource'
        console.log(e.url);         // the url whose request timed out
        casper.exit(1);
      }
    }
  };
}

// Scrape a URL using a ScraperJSON-defined scraper.
//
// @param {String} scrapeUrl the URL to scrape
// @param {Object} definition a dictionary defining the scraper
// @param {Function} cb callback to run once this scrape has finished
// @return {Object} a dictionary containing the scraping results
scrape.scrape = function(scrapeUrl, definition, cb, loglevel) {

  // validate arguments
  url.checkUrl(scrapeUrl);

  // let's get our scrape on
  var spooky = new Spooky(settings(loglevel), function() {
    spooky.start(scrapeUrl);

    spooky.then(function() {
      // in SpookyJS scope
      this.emit('pagedownload', this.evaluate(function() {
        // in rendered page scope
        return document.all[0].outerHTML;
      }));

    });

    spooky.run();
  });

  spooky.on('pagedownload', function(html) {
    log.debug('page downloaded and rendered');
    try {
      scrape.scrapeHtml(html, definition, scrapeUrl, cb);
    } catch(e) {
      log.error('problem scraping html:');
      log.error(e.message);
      log.error(e.stack);
    }
  });

  if (loglevel == 'debug') {
    spooky.on('console', function (line) {
      var parts = line.split(' ');
      log.debug(parts.slice(1, parts.length).join(' '));
    });

    spooky.on('log', function (log) {
      if (log.space === 'remote') {
        console.log(log.message.replace(/ \- .*/, ''));
      }
    });
  }

};

// Parse the rendered HTML using the scraper definition.
// Downloadable resources are saved to disk, and the successfully
// parsed elements are output as JSON.
//
// @param {String} html the HTML source of the rendered page
// @param {Object} definition the scraper definition
scrape.scrapeHtml = function(html, definition, scrapeUrl, cb) {
  var ticker = new Ticker(2, cb);

  // check rendered HTML
  if (!html) {
    log.error('error: SpookyJS failed to render the HTML');
  }

  // save the rendered html
  log.debug('saving rendered HTML');
  file.write('rendered.html', html, function(){ ticker.tick() });

  // load HTML into DOM
  log.debug('scraping rendered DOM');
  var doc = dom.render(html);

  // scrape the DOM using the ScraperJSON scraper
  var results = scrape.scrapeScraperJSON(definition,
                                         doc,
                                         scrapeUrl,
                                         cb,
                                         ticker);

  // save results JSON
  var json = JSON.stringify(results, undefined, 2);
  file.write('results.json', json, function(){ ticker.tick() });
}

scrape.scrapeScraperJSON =
  function(definition, doc, scrapeUrl, cb, ticker) {
  if (!ticker) {
    ticker = new Ticker(0, cb);
  }
  var results = [];

  // we don't know how many downloads will be spawned by each
  // element, so remove the Node.js cap of 10 listeners per
  // emitter (0 == infinty)
  process.setMaxListeners(0);

  for (var key in definition) {
    try {
      var element = definition[key];
      log.debug('scraping element:', key);
      // extract element
      var selector = element.selector;
      var attribute = element.attribute;
      var matches = dom.select(selector, doc);
      log.debug("found", matches.length, "matches");
      for (var i = 0; i < matches.length; i++) {
        var res = matches[i];
        if (res) {
          res = dom.getAttribute(res, attribute);
          log.data(key + ":", dom.cleanElement(res));

          // save the result
          var data = {};
          data[key] = res;
          results.push(data);

          // process downloads
          if (element.download) {
            // set download running
            var down = dl.downloadResource(res, scrapeUrl);
            // add it to the task ticker
            ticker.elongate();
            down.on('close', function() {
              log.debug('download done');
              ticker.tick();
            });
            down.on('error', function(err) {
              log.error('file download failed: ' + err);
              ticker.tick();
            });
          }
        } else {
          log.warn('could not capture element');
        }
      }
    } catch(err) {
      // errors deep in the stack should be presented, but
      // we want to continue with the rest of the execution
      log.error('problem applying scraperJSON scraper:');
      log.error(err.message);
      log.error(err.stack);
    }
  }
  if (results.length == 0) {
    log.warn('no elements were extracted from url:', scrapeUrl);
  }
  if (ticker.length > 2) {
    log.info('waiting for',
             ticker.length - 2,
             'downloads to complete in background');
  }
  return results;
}
<|endoftext|>


<commit_before>describe('Scoped CSS', function() {

  function render(str, parser) {
    return compiler.style(str, 'my-tag', parser || 'scoped-css')
  }

  it('add my-tag to the simple selector', function() {
    expect(render('h1 { font-size: 150% }'))
        .to.equal('my-tag h1,[riot-tag="my-tag"] h1 { font-size: 150% }')
  })
  it('add my-tag to the multi selector in a line', function() {
    expect(render('h1 { font-size: 150% } #id { color: #f00 }'))
        .to.equal('my-tag h1,[riot-tag="my-tag"] h1 { font-size: 150% } my-tag #id,[riot-tag="my-tag"] #id { color: #f00 }')
  })
  it('add my-tag to the complex selector', function() {
    expect(render('header a.button:hover { text-decoration: none }'))
        .to.equal('my-tag header a.button:hover,[riot-tag="my-tag"] header a.button:hover { text-decoration: none }')
  })
  it('add my-tag to the comma-separated selector', function() {
    expect(render('h2, h3 { border-bottom: 1px solid #000 }'))
        .to.equal('my-tag h2,[riot-tag="my-tag"] h2,my-tag h3,[riot-tag="my-tag"] h3 { border-bottom: 1px solid #000 }')
  })
  it('add my-tag to the attribute selector', function() {
    expect(render('i[class=twitter] { background: #55ACEE }'))
        .to.equal('my-tag i[class=twitter],[riot-tag="my-tag"] i[class=twitter] { background: #55ACEE }')
  })
  it('add my-tag to the selector with a backslash', function() {
    expect(render('a:after { content: "*" }'))
        .to.equal('my-tag a:after,[riot-tag="my-tag"] a:after { content: "*" }')
  })
  it('add my-tag to the selector with multi-line definitions', function() {
    expect(render('header {\n  text-align: center;\n  background: rgba(0,0,0,.2);\n}'))
        .to.equal('my-tag header,[riot-tag="my-tag"] header { text-align: center; background: rgba(0,0,0,.2); }')
  })
  it('add my-tag to the root selector', function() {
    expect(render(':scope { display: block }'))
        .to.equal('my-tag,[riot-tag="my-tag"] { display: block }')
  })
  it('add my-tag to the nested root selector', function() {
    expect(render(':scope > ul { padding: 0 }'))
        .to.equal('my-tag > ul,[riot-tag="my-tag"] > ul { padding: 0 }')
  })
  it('add my-tag to the root selector with attr', function() {
    expect(render(':scope[disabled] { color: gray }'))
        .to.equal('my-tag[disabled],[riot-tag="my-tag"][disabled] { color: gray }')
  })
  it('add my-tag to the root selector with class', function() {
    expect(render(':scope.great { color: gray }'))
        .to.equal('my-tag.great,[riot-tag="my-tag"].great { color: gray }')
  })
  it('not add my-tag to @font-face', function() {
    expect(render('@font-face { font-family: "FontAwesome" }'))
        .to.equal('@font-face { font-family: "FontAwesome" }')
  })
  it('not add my-tag to @media, and add it to the selector inside', function() {
    expect(render('@media (min-width: 500px) {\n  header {\n    text-align: left;\n  }\n}'))
        .to.equal('@media (min-width: 500px) { my-tag header,[riot-tag="my-tag"] header { text-align: left; } }')
  })
  it('not add my-tag to "from" and "to" in @keyframes', function() {
    expect(render('@keyframes fade { from { opacity: 1; } to { opacity: 0; } }'))
        .to.equal('@keyframes fade { from { opacity: 1; } to { opacity: 0; } }')
  })
  it('not add my-tag to parsentage values in @keyframes', function() {
    expect(render('@keyframes fade { 10% { opacity: 1; } 85% { opacity: 0; } }'))
        .to.equal('@keyframes fade { 10% { opacity: 1; } 85% { opacity: 0; } }')
  })

  it('use a custom css parser to render the css', function() {
    compiler.parsers.css.myParser = function(tag, css) {
      return css.replace(/@tag/, tag)
    }
    expect(render('@tag { color: red }', 'myParser'))
        .to.equal('my-tag { color: red }')
  })

})
<commit_msg>fix to test labels<commit_after>describe('Scoped CSS', function() {

  function render(str, parser) {
    return compiler.style(str, 'my-tag', parser || 'scoped-css')
  }

  it('add my-tag to the simple selector', function() {
    expect(render('h1 { font-size: 150% }'))
        .to.equal('my-tag h1,[riot-tag="my-tag"] h1 { font-size: 150% }')
  })
  it('add my-tag to the multi selector in a line', function() {
    expect(render('h1 { font-size: 150% } #id { color: #f00 }'))
        .to.equal('my-tag h1,[riot-tag="my-tag"] h1 { font-size: 150% } my-tag #id,[riot-tag="my-tag"] #id { color: #f00 }')
  })
  it('add my-tag to the complex selector', function() {
    expect(render('header a.button:hover { text-decoration: none }'))
        .to.equal('my-tag header a.button:hover,[riot-tag="my-tag"] header a.button:hover { text-decoration: none }')
  })
  it('add my-tag to the comma-separated selector', function() {
    expect(render('h2, h3 { border-bottom: 1px solid #000 }'))
        .to.equal('my-tag h2,[riot-tag="my-tag"] h2,my-tag h3,[riot-tag="my-tag"] h3 { border-bottom: 1px solid #000 }')
  })
  it('add my-tag to the attribute selector', function() {
    expect(render('i[class=twitter] { background: #55ACEE }'))
        .to.equal('my-tag i[class=twitter],[riot-tag="my-tag"] i[class=twitter] { background: #55ACEE }')
  })
  it('add my-tag to the selector with a pseudo-class', function() {
    expect(render('a:after { content: "*" }'))
        .to.equal('my-tag a:after,[riot-tag="my-tag"] a:after { content: "*" }')
  })
  it('add my-tag to the selector with multi-line definitions', function() {
    expect(render('header {\n  text-align: center;\n  background: rgba(0,0,0,.2);\n}'))
        .to.equal('my-tag header,[riot-tag="my-tag"] header { text-align: center; background: rgba(0,0,0,.2); }')
  })
  it('add my-tag to the root selector', function() {
    expect(render(':scope { display: block }'))
        .to.equal('my-tag,[riot-tag="my-tag"] { display: block }')
  })
  it('add my-tag to the nested root selector', function() {
    expect(render(':scope > ul { padding: 0 }'))
        .to.equal('my-tag > ul,[riot-tag="my-tag"] > ul { padding: 0 }')
  })
  it('add my-tag to the root selector with attribute', function() {
    expect(render(':scope[disabled] { color: gray }'))
        .to.equal('my-tag[disabled],[riot-tag="my-tag"][disabled] { color: gray }')
  })
  it('add my-tag to the root selector with class', function() {
    expect(render(':scope.great { color: gray }'))
        .to.equal('my-tag.great,[riot-tag="my-tag"].great { color: gray }')
  })
  it('not add my-tag to @font-face', function() {
    expect(render('@font-face { font-family: "FontAwesome" }'))
        .to.equal('@font-face { font-family: "FontAwesome" }')
  })
  it('not add my-tag to @media, and add it to the selector inside', function() {
    expect(render('@media (min-width: 500px) {\n  header {\n    text-align: left;\n  }\n}'))
        .to.equal('@media (min-width: 500px) { my-tag header,[riot-tag="my-tag"] header { text-align: left; } }')
  })
  it('not add my-tag to "from" and "to" in @keyframes', function() {
    expect(render('@keyframes fade { from { opacity: 1; } to { opacity: 0; } }'))
        .to.equal('@keyframes fade { from { opacity: 1; } to { opacity: 0; } }')
  })
  it('not add my-tag to parcentage values in @keyframes', function() {
    expect(render('@keyframes fade { 10% { opacity: 1; } 85% { opacity: 0; } }'))
        .to.equal('@keyframes fade { 10% { opacity: 1; } 85% { opacity: 0; } }')
  })

  it('use a custom css parser to render the css', function() {
    compiler.parsers.css.myParser = function(tag, css) {
      return css.replace(/@tag/, tag)
    }
    expect(render('@tag { color: red }', 'myParser'))
        .to.equal('my-tag { color: red }')
  })

})
<|endoftext|>


<commit_before>const Ajv = require('ajv');
const {
  Map,
  OrderedMap,
  Range,
} = require('immutable');
const math   = require('mathjs');
const moment = require('moment');

const Ratio = require('./ratio');

class Series {
  constructor(options = {}) {
    const now      = moment().startOf('second');
    const defaults = {
      from    : moment(now).subtract(1, 'hour').toISOString(),
      until   : now.toISOString(),
      interval: 1 * 60, // seconds
    };
    const schema = {
      $schema   : 'http://json-schema.org/schema#',
      type      : 'object',
      properties: {
        from    : {type: ['integer', 'string'], format: 'date-time', default: defaults.from },
        until   : {type: ['integer', 'string'], format: 'date-time', default: defaults.until},
        interval: {type: 'integer', default: defaults.interval},
      },
      additionalProperties: false,
    };
    const ajv     = new Ajv({useDefaults: true});
    const isValid = ajv.validate(schema, options);
    if (!isValid) {
      const error = ajv.errors[0];
      throw Error(`options${error.dataPath} ${error.message}`);
    }

    this.from     = moment(options.from );
    this.until    = moment(options.until);
    this.interval = moment.duration(options.interval, 'seconds');
  }

  _range() {
    return Range(this.from.unix(), this.until.unix() + 1, this.interval.asSeconds());
  }

  _trigonometric(func, options = {}) {
    const defaults = {
      keyName: 'value',

      coefficient: 1.0,
      constant   : 0.0,
      period     : 1 * 60 * 60, // seconds
    };
    const schema = {
      $schema   : 'http://json-schema.org/schema#',
      type: 'object',
      properties: {
        keyName: {type: 'string', default: defaults.keyName},

        coefficient: {type: 'number', default: defaults.coefficient},
        constant   : {type: 'number', default: defaults.constant   },
        period     : {type: 'integer', default: defaults.period},
      },
      additionalProperties: false,
    }
    const ajv     = new Ajv({useDefaults: true});
    const isValid = ajv.validate(schema, options);
    if (!isValid) {
      const error = ajv.errors[0];
      throw Error(`options${error.dataPath} ${error.message}`);
    }

    const scale = 2 * Math.PI / options.period;
    return this._range()
      .reduce((map, t) => map.set(moment(t * 1000).toISOString(), func(t * scale)), OrderedMap())
      .map((v) => options.coefficient * v + options.constant)
      .map((v) => math.round(v, 2))
      .map((v, t) => {
        return Map({timestamp: t}).set(options.keyName, v);
      })
      .toList()
      .toJS();
  }

  cos(options) {
    return this._trigonometric(Math.cos, options);
  }

  sin(options) {
    return this._trigonometric(Math.sin, options);
  }

  ratio(params, options = {}) {
    const ratio = new Ratio(params);
    const schema = {
      type      : 'object',
      properties: {
        keyName: {type: 'string', default: 'value'},
      },
      additionalProperties: false,
    };
    const ajv     = new Ajv({useDefaults: true});
    const isValid = ajv.validate(schema, options);
    if (!isValid) {
      const error = ajv.errors[0];
      throw Error(`options${error.dataPath} ${error.message}`);
    }

    return this._range()
      .map((t) => {
        return Map({timestamp: moment(t * 1000).toISOString()}).set(options.keyName, ratio.sample());
      })
      .toJS();
  }
}

module.exports = Series;
<commit_msg>Add Series._toElem()<commit_after>const Ajv = require('ajv');
const {
  Map,
  OrderedMap,
  Range,
} = require('immutable');
const math   = require('mathjs');
const moment = require('moment');

const Ratio = require('./ratio');

class Series {
  constructor(options = {}) {
    const now      = moment().startOf('second');
    const defaults = {
      from    : moment(now).subtract(1, 'hour').toISOString(),
      until   : now.toISOString(),
      interval: 1 * 60, // seconds
    };
    const schema = {
      $schema   : 'http://json-schema.org/schema#',
      type      : 'object',
      properties: {
        from    : {type: ['integer', 'string'], format: 'date-time', default: defaults.from },
        until   : {type: ['integer', 'string'], format: 'date-time', default: defaults.until},
        interval: {type: 'integer', default: defaults.interval},
      },
      additionalProperties: false,
    };
    const ajv     = new Ajv({useDefaults: true});
    const isValid = ajv.validate(schema, options);
    if (!isValid) {
      const error = ajv.errors[0];
      throw Error(`options${error.dataPath} ${error.message}`);
    }

    this.from     = moment(options.from );
    this.until    = moment(options.until);
    this.interval = moment.duration(options.interval, 'seconds');
  }

  _trigonometric(func, options = {}) {
    const defaults = {
      keyName: 'value',

      coefficient: 1.0,
      constant   : 0.0,
      period     : 1 * 60 * 60, // seconds
    };
    const schema = {
      $schema   : 'http://json-schema.org/schema#',
      type: 'object',
      properties: {
        keyName: {type: 'string', default: defaults.keyName},

        coefficient: {type: 'number', default: defaults.coefficient},
        constant   : {type: 'number', default: defaults.constant   },
        period     : {type: 'integer', default: defaults.period},
      },
      additionalProperties: false,
    }
    const ajv     = new Ajv({useDefaults: true});
    const isValid = ajv.validate(schema, options);
    if (!isValid) {
      const error = ajv.errors[0];
      throw Error(`options${error.dataPath} ${error.message}`);
    }

    const scale = 2 * Math.PI / options.period;
    return this._range()
      .reduce((map, t) => map.set(t, func(t * scale)), OrderedMap())
      .map((v) => options.coefficient * v + options.constant)
      .map((v) => math.round(v, 2))
      .map((v, t) => this._toElem(t, options.keyName, v))
      .toList()
      .toJS();
  }

  cos(options) {
    return this._trigonometric(Math.cos, options);
  }

  sin(options) {
    return this._trigonometric(Math.sin, options);
  }

  ratio(params, options = {}) {
    const ratio = new Ratio(params);
    const schema = {
      type      : 'object',
      properties: {
        keyName: {type: 'string', default: 'value'},
      },
      additionalProperties: false,
    };
    const ajv     = new Ajv({useDefaults: true});
    const isValid = ajv.validate(schema, options);
    if (!isValid) {
      const error = ajv.errors[0];
      throw Error(`options${error.dataPath} ${error.message}`);
    }

    return this._range()
      .map((t) => this._toElem(t, options.keyName, ratio.sample()))
      .toJS();
  }

  _range() {
    return Range(this.from.unix(), this.until.unix() + 1, this.interval.asSeconds());
  }

  _toElem(unix, keyName, value) {
    return Map({timestamp: moment(unix * 1000).toISOString()}).set(keyName, value);
  }
}

module.exports = Series;
<|endoftext|>


<commit_before>'use strict';

module.exports = server;

var node_path   = require('path');
var node_fs     = require('fs');
var node_url    = require('url');
var lang        = require('./lang');
var tools       = require('./tools');
var dao         = require('./dao');
var ejs         = require('ejs');

function server (options) {
    var user = options.user;
    var sys = options.sys;

    return function (req, res, next) {
        var req_data = server.apply_language(user, req.url);

        dao.current(req_data, options, function (err, data) {
            var current = data.current;
            var tree = data.tree;

            if ( !current ) {
                return next();
            }

            var content = server.render(sys.theme, {
                config  : user,
                tree    : tree,
                current : current,
                tools   : tools
            });

            res.send(content);
        });
    };
}

server.render = function (theme, data) {
    var template_file = node_path.join(theme, 'include', 'template', 'doc.ejs');
    var template_text = node_fs.readFileSync(template_file).toString();

    var template_fn = ejs.compile(template_text, {
        filename: template_file,
        cache: true
    });

    return template_fn(data);
};

server.apply_language = function (cfg, url) {
    url = node_url.parse(url, true);

    var slices = url.pathname.split('/').filter(Boolean);
    var language = slices[0];
    var languages = cfg.languages || {};

    var default_language = cfg.default_language;

    // req -> /docs/zh-CN/xxx/xxx
    if ( language in languages ) {
        slices.shift();

    // req -> /docs/xxx/xxx
    // -> /docs/<default-language>/xxx/xxx
    } else {
        language = default_language;
    }

    return {
        languages   : languages,
        language    : language,
        path_slices : slices,
        query       : url.query
    }
};<commit_msg>fix index template<commit_after>'use strict';

module.exports = server;

var node_path   = require('path');
var node_fs     = require('fs');
var node_url    = require('url');
var lang        = require('./lang');
var tools       = require('./tools');
var dao         = require('./dao');
var ejs         = require('ejs');

function server (options) {
    var user = options.user;
    var sys = options.sys;

    return function (req, res, next) {
        var req_data = server.apply_language(user, req.url);

        dao.current(req_data, options, function (err, data) {
            var current = data.current;
            var tree = data.tree;

            if ( !current ) {
                return next();
            }

            var content = server.render(sys.theme, {
                config  : user,
                tree    : tree,
                current : current,
                tools   : tools
            });

            res.send(content);
        });
    };
}

server.render = function (theme, data) {
    var template_file = node_path.join(theme, 'include', 'template', 'index.ejs');
    var template_text = node_fs.readFileSync(template_file).toString();

    var template_fn = ejs.compile(template_text, {
        filename: template_file,
        cache: true
    });

    return template_fn(data);
};

server.apply_language = function (cfg, url) {
    url = node_url.parse(url, true);

    var slices = url.pathname.split('/').filter(Boolean);
    var language = slices[0];
    var languages = cfg.languages || {};

    var default_language = cfg.default_language;

    // req -> /docs/zh-CN/xxx/xxx
    if ( language in languages ) {
        slices.shift();

    // req -> /docs/xxx/xxx
    // -> /docs/<default-language>/xxx/xxx
    } else {
        language = default_language;
    }

    return {
        languages   : languages,
        language    : language,
        path_slices : slices,
        query       : url.query
    }
};<|endoftext|>


<commit_before>var http = require("http"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    qs = require("querystring");
    utils = require("./utils"),
    exec = require('child_process').exec;

var mimeTypes = {
  "html": "text/html",
  "json": "text/json",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "png": "image/png",
  "js": "text/javascript",
  "css": "text/css"
};

exports.Server = function Server(bsClient, workers) {
  var status = 0;

  function handleFile (filename, request, response) {
    var url_parts = url.parse(request.url, true);
    var query = url_parts.query;

    if (query._worker_key && workers[query._worker_key]) {
      workers[query._worker_key].acknowledged = true;
      console.log("[%s] Acknowledged", query._browser_string);
    }

    fs.exists(filename, function(exists) {
      if(!exists) {
        response.writeHead(404, {"Content-Type": "text/plain"});
        response.write("404 Not Found\n");
        response.end();
        return;
      }

      fs.readFile(filename, "binary", function(err, file) {

        if(err) {
          response.writeHead(500, {"Content-Type": "text/plain"});
          response.write(err + "\n");
          response.end();
          return;
        }

        var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
        response.writeHead(200, mimeType);

        scripts = [
          'json2.js',
          'browserstack.js',
        ]

        framework_scripts = {'qunit': ['qunit-plugin.js'],
			     'jasmine': ['jasmine-jsreporter.js', 'jasmine-plugin.js'],
			     'mocha': ['mocha-plugin.js']
			    }

        if (mimeType === 'text/html') {
          var matcher = /(.*)<\/body>/;
          var patch = "$1";
          scripts.forEach(function (script) {
            patch += "\n";
          });

	  // adding framework scripts
	  if(config['test_framework'] && config['test_framework']=="jasmine"){
	    framework_scripts['jasmine'].forEach(function (script) {
	      patch += "\n";
	    });
	    patch +="\n";
	  }else if(config['test_framework'] && config['test_framework']=="mocha"){
	    framework_scripts['mocha'].forEach(function (script) {
	      patch += "\n";
	    });
	    patch +="\n";
	  }else{
	    framework_scripts['qunit'].forEach(function (script) {
	      patch += "\n";
	    });
	  }
          patch += "";

          file = file.replace(matcher, patch);
        }

        response.write(file);
        response.end();
      });
    });
  }

  function parseBody(body) {
    return JSON.parse(qs.parse(body).data);
  }

  handlers = {
    "_progress": function progressHandler(uri, body, request, response) {
      query = parseBody(body);

      var uuid = request.headers['x-worker-uuid'];
      var worker = workers[uuid];
      // console.log("Tests run:", query.tests_run);
      // if (query.tracebacks) {
      //   query.tracebacks.forEach(function (traceback) {
      //     console.log("Error:", traceback);
      //   });
      // }
      response.end();
    },
    "_report": function reportHandler(uri, body, request, response) {
      query = parseBody(body);
      var uuid = request.headers['x-worker-uuid'];
      var worker = workers[uuid];

      if (query.tracebacks.length > 0) {
        console.log("Tracebacks:");
        query.tracebacks.forEach(function (traceback) {
          console.log(traceback);
        });
      }

      console.log("[%s] Completed in %d milliseconds. %d of %d passed, %d failed.",
                  request.headers['x-browser-string'], query.runtime,
                  query.passed, query.total, query.failed);
      status += query.failed;

      if (worker) {
        bsClient.takeScreenshot(worker.id,function(error,screenshot){
          if(!error && screenshot.url){
              console.log('[%s] Screenshot: %s', worker.string, screenshot.url);
          }

        bsClient.terminateWorker(worker.id, function () {
          if (!workers[uuid]) {
            return;
          }

          console.log('[%s] Terminated', worker.string);

          clearTimeout(workers[uuid].activityTimeout);
          delete workers[uuid];

          if (utils.objectSize(workers) === 0) {
            console.log("All tests done, failures: %d.", status);

            if (status > 0) {
              status = 1;
            }

            process.exit(status);
          }
        });
      });
      }

      response.end();
    },
    "_log": function logHandler(uri, body, request, response) {
      query = parseBody(body);
      console.log('['+ request.headers['x-browser-string'] + '] ' + query);
      response.end();
    },
    "_patch": function patchHandler(uri, body, request, response) {
      handleFile(path.join(__dirname, uri), request, response);
    },
    "_default": function defaultHandler(uri, body, request, response) {
      handleFile(path.join(process.cwd(), uri), request, response);
    }
  }

  return http.createServer(function(request, response) {
    var uri = url.parse(request.url).pathname;
    var method = uri.split('/')[1];
    var filename;

    var body = '';

    request.on('data', function (data) {
      body += data;
    });
    request.on('end', function () {
      (handlers[method] || handlers._default)(uri, body, request,
                                              response);
    });
  });
};
<commit_msg>not able to parse mocha empty response. sometimes mocha is sending empty response<commit_after>var http = require("http"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    qs = require("querystring");
    utils = require("./utils"),
    exec = require('child_process').exec;

var mimeTypes = {
  "html": "text/html",
  "json": "text/json",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "png": "image/png",
  "js": "text/javascript",
  "css": "text/css"
};

exports.Server = function Server(bsClient, workers) {
  var status = 0;

  function handleFile (filename, request, response) {
    var url_parts = url.parse(request.url, true);
    var query = url_parts.query;

    if (query._worker_key && workers[query._worker_key]) {
      workers[query._worker_key].acknowledged = true;
      console.log("[%s] Acknowledged", query._browser_string);
    }

    fs.exists(filename, function(exists) {
      if(!exists) {
        response.writeHead(404, {"Content-Type": "text/plain"});
        response.write("404 Not Found\n");
        response.end();
        return;
      }

      fs.readFile(filename, "binary", function(err, file) {

        if(err) {
          response.writeHead(500, {"Content-Type": "text/plain"});
          response.write(err + "\n");
          response.end();
          return;
        }

        var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
        response.writeHead(200, mimeType);

        scripts = [
          'json2.js',
          'browserstack.js',
        ]

        framework_scripts = {'qunit': ['qunit-plugin.js'],
			     'jasmine': ['jasmine-jsreporter.js', 'jasmine-plugin.js'],
			     'mocha': ['mocha-plugin.js']
			    }

        if (mimeType === 'text/html') {
          var matcher = /(.*)<\/body>/;
          var patch = "$1";
          scripts.forEach(function (script) {
            patch += "\n";
          });

	  // adding framework scripts
	  if(config['test_framework'] && config['test_framework']=="jasmine"){
	    framework_scripts['jasmine'].forEach(function (script) {
	      patch += "\n";
	    });
	    patch +="\n";
	  }else if(config['test_framework'] && config['test_framework']=="mocha"){
	    framework_scripts['mocha'].forEach(function (script) {
	      patch += "\n";
	    });
	    patch +="\n";
	  }else{
	    framework_scripts['qunit'].forEach(function (script) {
	      patch += "\n";
	    });
	  }
          patch += "";

          file = file.replace(matcher, patch);
        }

        response.write(file);
        response.end();
      });
    });
  }

  function parseBody(body) {
    return JSON.parse(qs.parse(body).data);
  }

  handlers = {
    "_progress": function progressHandler(uri, body, request, response) {
      query = parseBody(body);

      var uuid = request.headers['x-worker-uuid'];
      var worker = workers[uuid];
      // console.log("Tests run:", query.tests_run);
      // if (query.tracebacks) {
      //   query.tracebacks.forEach(function (traceback) {
      //     console.log("Error:", traceback);
      //   });
      // }
      response.end();
    },
    "_report": function reportHandler(uri, body, request, response) {
      query = null;
      try {query = parseBody(body); }catch(e){}
      var uuid = request.headers['x-worker-uuid'];
      var worker = workers[uuid];

      if (query == null) {
        console.log("[%s] Null response from remote Browser", request.headers['x-browser-string']);
      } else {
      if (query.tracebacks.length > 0) {
        console.log("Tracebacks:");
        query.tracebacks.forEach(function (traceback) {
          console.log(traceback);
        });
      }

      console.log("[%s] Completed in %d milliseconds. %d of %d passed, %d failed.",
                  request.headers['x-browser-string'], query.runtime,
                  query.passed, query.total, query.failed);
      status += query.failed;
      }

      if (worker) {
        bsClient.takeScreenshot(worker.id,function(error,screenshot){
          if(!error && screenshot.url){
              console.log('[%s] Screenshot: %s', worker.string, screenshot.url);
          }

        bsClient.terminateWorker(worker.id, function () {
          if (!workers[uuid]) {
            return;
          }

          console.log('[%s] Terminated', worker.string);

          clearTimeout(workers[uuid].activityTimeout);
          delete workers[uuid];

          if (utils.objectSize(workers) === 0) {
            console.log("All tests done, failures: %d.", status);

            if (status > 0) {
              status = 1;
            }

            process.exit(status);
          }
        });
      });
      }

      response.end();
    },
    "_log": function logHandler(uri, body, request, response) {
      query = parseBody(body);
      console.log('['+ request.headers['x-browser-string'] + '] ' + query);
      response.end();
    },
    "_patch": function patchHandler(uri, body, request, response) {
      handleFile(path.join(__dirname, uri), request, response);
    },
    "_default": function defaultHandler(uri, body, request, response) {
      handleFile(path.join(process.cwd(), uri), request, response);
    }
  }

  return http.createServer(function(request, response) {
    var uri = url.parse(request.url).pathname;
    var method = uri.split('/')[1];
    var filename;

    var body = '';

    request.on('data', function (data) {
      body += data;
    });
    request.on('end', function () {
      (handlers[method] || handlers._default)(uri, body, request,
                                              response);
    });
  });
};
<|endoftext|>


<commit_before>// Native
const path = require('path')
const { parse, format } = require('url')

// Packages
const micro = require('micro')
const auth = require('basic-auth')
const { red } = require('chalk')
const fs = require('fs-extra')
const pathType = require('path-type')
const mime = require('mime-types')
const stream = require('send')
const { coroutine } = require('bluebird')

// Ours
const renderDirectory = require('./render')

module.exports = coroutine(function*(req, res, flags, current, ignoredFiles) {
  const headers = {}

  if (flags.cors) {
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Headers'] =
      'Origin, X-Requested-With, Content-Type, Accept, Range'
  }

  for (const header in headers) {
    if (!{}.hasOwnProperty.call(headers, header)) {
      continue
    }

    res.setHeader(header, headers[header])
  }

  if (flags.auth) {
    const credentials = auth(req)

    if (!process.env.SERVE_USER || !process.env.SERVE_PASSWORD) {
      console.error(
        red(
          'You are running serve with basic auth but did not set the SERVE_USER and SERVE_PASSWORD environment variables.'
        )
      )

      // eslint-disable-next-line unicorn/no-process-exit
      process.exit(1)
    }

    if (
      !credentials ||
      credentials.name !== process.env.SERVE_USER ||
      credentials.pass !== process.env.SERVE_PASSWORD
    ) {
      res.statusCode = 401
      res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm"')
      return micro.send(res, 401, 'Access Denied')
    }
  }

  const { pathname } = parse(req.url)
  const assetDir = path.normalize(process.env.ASSET_DIR)

  let related = path.parse(path.join(current, pathname))
  let assetRequest = false

  if (related.dir.indexOf(assetDir) > -1) {
    assetRequest = true
    const relative = path.relative(assetDir, pathname)
    related = path.parse(path.join(__dirname, '/../assets', relative))
  }

  related = decodeURIComponent(path.format(related))
  let notFoundResponse = 'Not Found'

  try {
    const custom404Path = path.join(current, '/404.html')
    notFoundResponse = yield fs.readFile(custom404Path, 'utf-8')
  } catch (err) {}

  // Don't allow rendering ignored files
  const ignored = !ignoredFiles.every(item => {
    return !pathname.includes(item)
  })

  if (ignored || (!assetRequest && related.indexOf(current) !== 0)) {
    return micro.send(res, 404, notFoundResponse)
  }

  const relatedExists = yield fs.exists(related)

  if (!relatedExists && !flags.single) {
    return micro.send(res, 404, notFoundResponse)
  }

  const streamOptions = {}

  if (flags.cache) {
    streamOptions.maxAge = flags.cache
  } else if (flags.single) {
    // Cache assets of single page applications for a day.
    // Later in the code, we'll define that `index.html` never
    // gets cached!
    streamOptions.maxAge = 8640000
  }

  // Check if directory
  if (relatedExists && (yield pathType.dir(related))) {
    // Normalize path to trailing slash
    // Otherwise problems like #70 will occur
    const url = parse(req.url)

    if (url.pathname.substr(-1) !== '/') {
      url.pathname += '/'
      const newPath = format(url)

      res.writeHead(302, {
        Location: newPath
      })

      res.end()
      return
    }

    let indexPath = path.join(related, '/index.html')
    res.setHeader('Content-Type', mime.contentType(path.extname(indexPath)))

    if (!(yield fs.exists(indexPath))) {
      // Try to render the current directory's content
      const port = flags.port || req.socket.localPort
      const renderedDir = yield renderDirectory(
        port,
        current,
        related,
        ignoredFiles
      )

      // If it works, send the directory listing to the user
      if (renderedDir) {
        return micro.send(res, 200, renderedDir)
      }

      // And if it doesn't, see if it's a single page application
      // If that's not true either, send an error
      if (!flags.single) {
        return micro.send(res, 404, notFoundResponse)
      }

      // But IF IT IS true, load the SPA's root index file
      indexPath = path.join(current, '/index.html')
    }

    return stream(req, indexPath, streamOptions).pipe(res)
  }

  if (!(yield fs.exists(related)) && flags.single) {
    // Don't cache the `index.html` file for single page applications
    streamOptions.maxAge = 0

    // Load `index.html` and send it to the client
    const indexPath = path.join(current, '/index.html')
    return stream(req, indexPath, streamOptions).pipe(res)
  }

  // Serve files without a mime type as html for SPA, and text for non SPA
  // eslint-disable-next-line camelcase
  stream.mime.default_type = flags.single ? 'text/html' : 'text/plain'

  return stream(
    req,
    related,
    Object.assign(
      {
        dotfiles: 'allow'
      },
      streamOptions
    )
  ).pipe(res)
})
<commit_msg>Shorter error message for missing authentication flags<commit_after>// Native
const path = require('path')
const { parse, format } = require('url')

// Packages
const micro = require('micro')
const auth = require('basic-auth')
const { red } = require('chalk')
const fs = require('fs-extra')
const pathType = require('path-type')
const mime = require('mime-types')
const stream = require('send')
const { coroutine } = require('bluebird')

// Ours
const renderDirectory = require('./render')

module.exports = coroutine(function*(req, res, flags, current, ignoredFiles) {
  const headers = {}

  if (flags.cors) {
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Headers'] =
      'Origin, X-Requested-With, Content-Type, Accept, Range'
  }

  for (const header in headers) {
    if (!{}.hasOwnProperty.call(headers, header)) {
      continue
    }

    res.setHeader(header, headers[header])
  }

  if (flags.auth) {
    const credentials = auth(req)

    if (!process.env.SERVE_USER || !process.env.SERVE_PASSWORD) {
      const error =
        'The environment variables "SERVE_USER" ' +
        'and/or "SERVE_PASSWORD" are missing!'
      console.error(red(error))

      // eslint-disable-next-line unicorn/no-process-exit
      process.exit(1)
    }

    if (
      !credentials ||
      credentials.name !== process.env.SERVE_USER ||
      credentials.pass !== process.env.SERVE_PASSWORD
    ) {
      res.statusCode = 401
      res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm"')
      return micro.send(res, 401, 'Access Denied')
    }
  }

  const { pathname } = parse(req.url)
  const assetDir = path.normalize(process.env.ASSET_DIR)

  let related = path.parse(path.join(current, pathname))
  let assetRequest = false

  if (related.dir.indexOf(assetDir) > -1) {
    assetRequest = true
    const relative = path.relative(assetDir, pathname)
    related = path.parse(path.join(__dirname, '/../assets', relative))
  }

  related = decodeURIComponent(path.format(related))
  let notFoundResponse = 'Not Found'

  try {
    const custom404Path = path.join(current, '/404.html')
    notFoundResponse = yield fs.readFile(custom404Path, 'utf-8')
  } catch (err) {}

  // Don't allow rendering ignored files
  const ignored = !ignoredFiles.every(item => {
    return !pathname.includes(item)
  })

  if (ignored || (!assetRequest && related.indexOf(current) !== 0)) {
    return micro.send(res, 404, notFoundResponse)
  }

  const relatedExists = yield fs.exists(related)

  if (!relatedExists && !flags.single) {
    return micro.send(res, 404, notFoundResponse)
  }

  const streamOptions = {}

  if (flags.cache) {
    streamOptions.maxAge = flags.cache
  } else if (flags.single) {
    // Cache assets of single page applications for a day.
    // Later in the code, we'll define that `index.html` never
    // gets cached!
    streamOptions.maxAge = 8640000
  }

  // Check if directory
  if (relatedExists && (yield pathType.dir(related))) {
    // Normalize path to trailing slash
    // Otherwise problems like #70 will occur
    const url = parse(req.url)

    if (url.pathname.substr(-1) !== '/') {
      url.pathname += '/'
      const newPath = format(url)

      res.writeHead(302, {
        Location: newPath
      })

      res.end()
      return
    }

    let indexPath = path.join(related, '/index.html')
    res.setHeader('Content-Type', mime.contentType(path.extname(indexPath)))

    if (!(yield fs.exists(indexPath))) {
      // Try to render the current directory's content
      const port = flags.port || req.socket.localPort
      const renderedDir = yield renderDirectory(
        port,
        current,
        related,
        ignoredFiles
      )

      // If it works, send the directory listing to the user
      if (renderedDir) {
        return micro.send(res, 200, renderedDir)
      }

      // And if it doesn't, see if it's a single page application
      // If that's not true either, send an error
      if (!flags.single) {
        return micro.send(res, 404, notFoundResponse)
      }

      // But IF IT IS true, load the SPA's root index file
      indexPath = path.join(current, '/index.html')
    }

    return stream(req, indexPath, streamOptions).pipe(res)
  }

  if (!(yield fs.exists(related)) && flags.single) {
    // Don't cache the `index.html` file for single page applications
    streamOptions.maxAge = 0

    // Load `index.html` and send it to the client
    const indexPath = path.join(current, '/index.html')
    return stream(req, indexPath, streamOptions).pipe(res)
  }

  // Serve files without a mime type as html for SPA, and text for non SPA
  // eslint-disable-next-line camelcase
  stream.mime.default_type = flags.single ? 'text/html' : 'text/plain'

  return stream(
    req,
    related,
    Object.assign(
      {
        dotfiles: 'allow'
      },
      streamOptions
    )
  ).pipe(res)
})
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var net = require("net")
  , fs = require("fs")
  , path = require("path")
  , EventEmitter = require("events").EventEmitter
  , inherits = require("util").inherits
  , sni = require("sni")
  , Batch = require("batch")
  , mkdirp = require("mkdirp")
  , util = require("./util");

/**
 * Expose `Server`.
 * @type {Server}
 */
module.exports = Server;

/**
 * Server
 * @param {Distroy} distroy reference
 * @param {String}  protocol
 * @param {Number}  port
 * @extends {events.EventEmitter}
 */
inherits(Server, EventEmitter);
function Server(distroy, protocol, port) {
  if(!(this instanceof Server)) return new Server(distroy, protocol, port);
  if(!~["http", "https"].indexOf(protocol)) throw new Error("Wrong protocol: " + protocol);

  EventEmitter.call(this);

  this.distroy = distroy;
  this.protocol = protocol;
  this.port = port;
  this.hosts = {};
  this.sendRemoteAddress = false;
  this.server = net.createServer(onConnect.bind(this)).listen(this.port);
}

/**
 * Handle a new connection.
 * @param  {net.Socket} socket
 * @this {Server}
 * @private
 */
function onConnect(socket) {
  var self = this;
  socket.once("readable", function() {
    var buf = socket.read()
      , host;
    if(!Buffer.isBuffer(buf)) return;
    socket.unshift(buf);

    switch(self.protocol) {
      case "https":
        host = sni(buf);
        break;
      case "http":
        host = buf.toString("utf8").match(httpRegExp);
        if(host instanceof Array) host = host[1];
        break;
    }

    if(typeof host !== "string") {
      socket.emit("exception", new Error("Could not determine host from " + socket.remoteAddress), socket);
      socket.destroy();
      return;
    }

    self.proxy(socket, host);
  });
  socket.on("error", self.emit.bind(self, "exception"));
}

var httpRegExp = /^Host:\s+(.*)$/mi;

/**
 * Proxy a connection.
 * @param  {net.Socket} socket
 * @param  {String}     host
 * @return {Server}            this
 */
Server.prototype.proxy = function(socket, host) {
  host = this.get(host);
  if(host === null) return;

  var connection;
  if(typeof host.path === "string")
    connection = net.createConnection(host.path);
  else if(typeof host.host === "string")
    connection = net.createConnection(util.defaultPort(this, host.port), host.host);
  else if(typeof host.port === "number")
    connection = net.createConnection("localhost", host.port);

  if(!connection) return socket.emit("exception", new Error("Could not establish a connection"), host);
  connection.on("error", this.emit.bind(this, "exception"));

  if(this.sendRemoteAddress) {
    connection.write(util.ipToBuffer(socket.remoteAddress));
    connection.write(util.portToBuffer(socket.remotePort));
  }
  socket.pipe(connection).pipe(socket);

  return this;
};

/**
 * Wether to prepend the IP Address.
 * @param  {Boolean} send
 * @return {Server}       this
 */
Server.prototype.ip =
Server.prototype.sendIP = function(send) {
  if(typeof send === "undefined") this.sendRemoteAddress = true;
  else this.sendRemoteAddress = !!send;
  return this;
};

/**
 * Find a host.
 * @param  {String} host
 * @return {Object|null}
 */
Server.prototype.get =
Server.prototype.getHost = function(host) {
  var match;
  for(var key in this.hosts)
      match = this.hosts[key];

  if(match) return match;
  this.emit("exception", new Error("Could not find a host for " + host));
  return null;
};

/**
 * Add a host.
 * @param  {String} inHost
 * @param  {String} outHost
 * @param  {Number} outPort
 * @return {Server}         this
 */
Server.prototype.add =
Server.prototype.addHost = function(inHost, outHost, outPort) {
  var host = { key: inHost };
  if(arguments.length === 1) { // unix domain socket
    host.regex = util.extractHost(inHost);
    host.path = inHost;
  } else if(arguments.length === 2) {
    host.regex = util.extractHost(inHost);
    outPort = parseInt(outHost, 10);
    if(outPort) { // inHost, outPort
      host.host = "localhost";
      host.port = outPort;
    } else { // inHost, outHost/unix
      if((/\//).test(outHost)) { // inHost, unix
        host.path = outHost;
      } else { // inHost, outHost
        host.host = outHost;
        host.port = util.defaultPort(this);
      }
    }
  } else if(arguments.length === 3) { // inHost, outHost, outPort
    host.regex = util.extractHost(inHost);
    host.host = outHost;
    host.port = outPort;
  } else {
    throw new Error("Wrong arity for addHost(inHost, outHost, outPort)");
  }
  if(host.key in this.hosts) throw new Error("Host `" + host.key + "` alread exists");
  this.hosts[host.key] = host;
  return this;
};

/**
 * Remove a host.
 * @param  {String} key
 * @return {Server}     this
 */
Server.prototype.remove =
Server.prototype.removeHost = function(key) {
  delete this.hosts[key];
  return this;
};

/**
 * Start or stop ping checking.
 * @param  {Number} interval
 * @param  {Number} timeout
 * @return {Server}          this
 */
Server.prototype.ping = function(interval, timeout) {
  if(interval === false) {
    clearInterval(this.pinger);
    return this;
  }

  this.pinger = setInterval(ping.bind(this, timeout || 5 * 1000), interval || 60 * 1000);
};
function ping(timeout) {
  var batch = new Batch().concurrency(4);
  for(key in this.hosts)
    batch.push((function(host) {
      var connection;
      if(typeof host.path === "string")
        connection = net.createConnection(host.path);
      else if(typeof host.host === "string")
        connection = net.createConnection(util.defaultPort(this, host.port), host.host);
      else if(typeof host.port === "number")
        connection = net.createConnection("localhost", host.port);

      connection.setTimeout(timeout, error.bind(socket, host));
      connection.on("error", error.bind(socket, host));
      connection.on("connect", success.bind(socket, host));
    }).bind(this, this.hosts[key]));
  batch.end();

  function error(host) {
    this.destroy();
    if(host.path) fs.unlink(host.path);
  }
  function success(host) {
    this.close();
  }
}

/**
 * Watch a directory for unix socket paths.
 * @param  {String} dir
 * @param  {Number} interval
 * @return {Server}          this
 */
Server.prototype.watch = function(dir, interval) {
  mkdirp.sync(dir, 0777);
  fs.chmodSync(dir, 0777);

  var self = this;
  function traverseHosts() {
    fs.readdirSync(dir).forEach(function(file) {
      var key = path.join(dir, file);
      if(!(key in self.hosts)) self.add(key);
    });
  }
  setInterval(traverseHosts, interval || 3000);
  traverseHosts();

  return this;
};<commit_msg>Fix naming<commit_after>/**
 * Module dependencies.
 */
var net = require("net")
  , fs = require("fs")
  , path = require("path")
  , EventEmitter = require("events").EventEmitter
  , inherits = require("util").inherits
  , sni = require("sni")
  , Batch = require("batch")
  , mkdirp = require("mkdirp")
  , util = require("./util");

/**
 * Expose `Server`.
 * @type {Server}
 */
module.exports = Server;

/**
 * Server
 * @param {Distroy} distroy reference
 * @param {String}  protocol
 * @param {Number}  port
 * @extends {events.EventEmitter}
 */
inherits(Server, EventEmitter);
function Server(distroy, protocol, port) {
  if(!(this instanceof Server)) return new Server(distroy, protocol, port);
  if(!~["http", "https"].indexOf(protocol)) throw new Error("Wrong protocol: " + protocol);

  EventEmitter.call(this);

  this.distroy = distroy;
  this.protocol = protocol;
  this.port = port;
  this.hosts = {};
  this.sendRemoteAddress = false;
  this.server = net.createServer(onConnect.bind(this)).listen(this.port);
}

/**
 * Handle a new connection.
 * @param  {net.Socket} socket
 * @this {Server}
 * @private
 */
function onConnect(socket) {
  var self = this;
  socket.once("readable", function() {
    var buf = socket.read()
      , host;
    if(!Buffer.isBuffer(buf)) return;
    socket.unshift(buf);

    switch(self.protocol) {
      case "https":
        host = sni(buf);
        break;
      case "http":
        host = buf.toString("utf8").match(httpRegExp);
        if(host instanceof Array) host = host[1];
        break;
    }

    if(typeof host !== "string") {
      socket.emit("exception", new Error("Could not determine host from " + socket.remoteAddress), socket);
      socket.destroy();
      return;
    }

    self.proxy(socket, host);
  });
  socket.on("error", self.emit.bind(self, "exception"));
}

var httpRegExp = /^Host:\s+(.*)$/mi;

/**
 * Proxy a connection.
 * @param  {net.Socket} socket
 * @param  {String}     host
 * @return {Server}            this
 */
Server.prototype.proxy = function(socket, host) {
  host = this.get(host);
  if(host === null) return;

  var connection;
  if(typeof host.path === "string")
    connection = net.createConnection(host.path);
  else if(typeof host.host === "string")
    connection = net.createConnection(util.defaultPort(this, host.port), host.host);
  else if(typeof host.port === "number")
    connection = net.createConnection("localhost", host.port);

  if(!connection) return socket.emit("exception", new Error("Could not establish a connection"), host);
  connection.on("error", this.emit.bind(this, "exception"));

  if(this.sendRemoteAddress) {
    connection.write(util.ipToBuffer(socket.remoteAddress));
    connection.write(util.portToBuffer(socket.remotePort));
  }
  socket.pipe(connection).pipe(socket);

  return this;
};

/**
 * Wether to prepend the IP Address.
 * @param  {Boolean} send
 * @return {Server}       this
 */
Server.prototype.ip =
Server.prototype.sendIP = function(send) {
  if(typeof send === "undefined") this.sendRemoteAddress = true;
  else this.sendRemoteAddress = !!send;
  return this;
};

/**
 * Find a host.
 * @param  {String} host
 * @return {Object|null}
 */
Server.prototype.get =
Server.prototype.getHost = function(host) {
  var match;
  for(var key in this.hosts)
      match = this.hosts[key];

  if(match) return match;
  this.emit("exception", new Error("Could not find a host for " + host));
  return null;
};

/**
 * Add a host.
 * @param  {String} inHost
 * @param  {String} outHost
 * @param  {Number} outPort
 * @return {Server}         this
 */
Server.prototype.add =
Server.prototype.addHost = function(inHost, outHost, outPort) {
  var host = { key: inHost };
  if(arguments.length === 1) { // unix domain socket
    host.regex = util.extractHost(inHost);
    host.path = inHost;
  } else if(arguments.length === 2) {
    host.regex = util.extractHost(inHost);
    outPort = parseInt(outHost, 10);
    if(outPort) { // inHost, outPort
      host.host = "localhost";
      host.port = outPort;
    } else { // inHost, outHost/unix
      if((/\//).test(outHost)) { // inHost, unix
        host.path = outHost;
      } else { // inHost, outHost
        host.host = outHost;
        host.port = util.defaultPort(this);
      }
    }
  } else if(arguments.length === 3) { // inHost, outHost, outPort
    host.regex = util.extractHost(inHost);
    host.host = outHost;
    host.port = outPort;
  } else {
    throw new Error("Wrong arity for addHost(inHost, outHost, outPort)");
  }
  if(host.key in this.hosts) throw new Error("Host `" + host.key + "` alread exists");
  this.hosts[host.key] = host;
  return this;
};

/**
 * Remove a host.
 * @param  {String} key
 * @return {Server}     this
 */
Server.prototype.remove =
Server.prototype.removeHost = function(key) {
  delete this.hosts[key];
  return this;
};

/**
 * Start or stop ping checking.
 * @param  {Number} interval
 * @param  {Number} timeout
 * @return {Server}          this
 */
Server.prototype.ping = function(interval, timeout) {
  if(interval === false) {
    clearInterval(this.pinger);
    return this;
  }

  this.pinger = setInterval(ping.bind(this, timeout || 5 * 1000), interval || 60 * 1000);
};
function ping(timeout) {
  var batch = new Batch().concurrency(4);
  for(key in this.hosts)
    batch.push((function(host) {
      var connection;
      if(typeof host.path === "string")
        connection = net.createConnection(host.path);
      else if(typeof host.host === "string")
        connection = net.createConnection(util.defaultPort(this, host.port), host.host);
      else if(typeof host.port === "number")
        connection = net.createConnection("localhost", host.port);

      connection.setTimeout(timeout, error.bind(connection, host));
      connection.on("error", error.bind(connection, host));
      connection.on("connect", success.bind(connection, host));
    }).bind(this, this.hosts[key]));
  batch.end();

  function error(host) {
    this.destroy();
    if(host.path) fs.unlink(host.path);
  }
  function success(host) {
    this.close();
  }
}

/**
 * Watch a directory for unix socket paths.
 * @param  {String} dir
 * @param  {Number} interval
 * @return {Server}          this
 */
Server.prototype.watch = function(dir, interval) {
  mkdirp.sync(dir, 0777);
  fs.chmodSync(dir, 0777);

  var self = this;
  function traverseHosts() {
    fs.readdirSync(dir).forEach(function(file) {
      var key = path.join(dir, file);
      if(!(key in self.hosts)) self.add(key);
    });
  }
  setInterval(traverseHosts, interval || 3000);
  traverseHosts();

  return this;
};<|endoftext|>