<commit_before>var juice = require('../juice'),
    getCaller = juice.Utils.getCaller,
    deprecated = juice.Utils.deprecationWarning,
    formatHTTPDate = juice.Utils.formatHTTPDate;

/**
 *  class juice.Context
 *
 *  Per request context. Is a 'poor-mans-subclass' of the application.
 **/
var Context = exports.Context = function Context(app, request) {

  function AppContext(request) {
    this.request = request;
  }
  AppContext.prototype = app;

  var ctx = new AppContext(request);

  // Now copy things from the Context.prototype to ctx object -- poor mans MI
  for (var i in Context.prototype) {
    if (Context.prototype.hasOwnProperty(i))
      ctx[i] = Context.prototype[i];
  }

  ctx.init( request );

  return ctx;

}

Context.prototype.init = function( ) {
  var ctx = this;

  var j_env = { docRoot: ctx.docRoot };
  if ("env" in ctx.request == false)
    ctx.request.env = { juice: j_env };
  else
    ctx.request.env.juice = j_env;

  ctx.hostUrl = ctx.request.scheme + "://";

  if ( ctx.request.headers.host )
    ctx.hostUrl += ctx.request.headers.host
  else {
    ctx.hostUrl += ctx.request.host;
    if ( ctx.request.port )
      ctx.hostUrl += ":" + ctx.request.port;
  }

  j_env.hostUrl = ctx.hostUrl;

  // Back compat for 0.1: warn in 0.2, remove in 0.3
  Object.defineProperty(this, "base", {
    getter: function() {
      deprecated("Context#base", "0.3");
      return ctx.hostUrl
    },
    setter: function( x ) {
      deprecated("Context#base", "0.3");
      return ctx.hostUrl = x;
    }
  } );

  Object.defineProperty(this, "redirectTo", {
    setter: function( x ) {
      deprecated("Context#redirectTo", "0.3");
      this.res.redirect( this.absUrlFor( x ), 302 );
    }
  } );

  ctx.response = new juice.Response(ctx);

  // Alias
  Object.defineProperty(this, "res", {
    getter: function() { return this.response },
    setter: function( x ) { return this.response = x }
  } );

  // Pull params into objects
  ctx.buildParams();
}

Context.prototype.runAction = function runAction(captures, action) {
  // decode parameters (i.e. "%20" -> " ")
  captures = captures.map( function( c ) decodeURIComponent( c ) );

  var data = action.action.apply(this, captures),
      res = this.response,
      redirect, template;

  // Make sure there is a merge funciton.
  if ( "merge" in res == false)
    res.merge = juice.Response.prototype.merge;

  if (action.raw) {
    res = data;
  }
  else if (res.file) {
    if (! res.headers[ 'last-modified' ]) {
      // Do it here rather than in merge to avoid a needless stat if its provided
      this.response.headers[ 'last-modified' ] = formatHTTPDate(
        filesystem.lastModified( res.file )
      );
    }

    // TODO: Do Server sniffing to make this work in more places
    res.headers[ 'x-sendfile' ] = res.file;
    res = res.merge( {
      status: 200,
      headers: {},
      body: []
    } );
  }
  else if (this.format == "json") {
    res = res.merge( {
      status: 200,
      headers: { contentType : 'application/json' },
      body: [ JSON.stringify( data ) ]
    } );
  }
  else if ( "status" in res == false && ( redirect = action.redirect ) ) {
    // If res.status is already set, dont redirect.
    res = res.merge( {
      status: 302,
      headers: { location: this.absUrlFor( redirect ) },
      body: []
    } );
  }
  else if (action.render) {
    data.juice = this.helpers;
    data.juice.urlFor = this.urlFor.bind(this);
    data.juice.abUrlFor = this.absUrlFor.bind(this);

    var rendered = this.renderTemplate( action.render, data );

    if ( rendered === undefined )
      throw "Couldn't find template file to load";

    res = res.merge( {
      status: 200,
      headers: { contentType : 'text/html' },
      body: [ rendered ]
    } );
  }
  else if ("status" in res == false || res.status < 300 && res.status > 399 ) {
    // Redirect actions dont need content
    throw new Error( "You need to specify a redirect or a template to render" );
  }
  else {
    res = res.merge( {
      body: [],
      headers: {}
    } );
  }

  return res;
}

Context.prototype.absUrlFor = function() {
  return this.hostUrl + this.urlFor.apply(this, arguments);
}

Context.prototype.urlFor = function() {
  // TODO: Better url handling and deal with absolute arumgents.
  var url = Array.join(arguments, '/');
  url = url.replace(/\/+/g, '/')
           .replace(/^[^\/]/, '/');

  return this.request.scriptName + url;
}

Context.prototype.buildParams = function buildParams() {
  var store = this.request.env.juice;
  store.params = {
    get: this.request.queryString ? juice.Utils.splitQueryString(this.request.queryString) : {},
    post: {}
  };

  var len = this.request.headers['content-length'];
  if (len && this.request.method == "POST" &&
      this.request.headers['content-type'] == 'application/x-www-form-urlencoded')
  {
    var body = this.request.input.read(len).decodeToString();
    store.params.post = juice.Utils.splitQueryString(body);
  }

  this.params = store.params;
}

Context.prototype.renderTemplate = function( file, stash ) {
  // load templates config or default to TT if not available
  var conf = this.config.templates || { "tt" : "tt" };

  const fs = require( 'fs-base' );

  // check each include path in order
  for ( let i = 0; i < this.templatePath.length; ++i ) {
    // check each possible extension in order
    for ( let ext in conf ) {
      // build the path to the template being search for
      var path = this.templatePath[ i ] + file + '.' + ext;

      // if the template exists, render it with the right engine and return
      if ( fs.isFile( path ) ) {
        let template = fs.rawOpen( path, 'r' ).readWhole();

        // load the adaptor for the engine
        let Adaptor = this.loadRelativeComponent( "./view/" + conf[ ext ] ).View,
            engine = new Adaptor( { paths : this.templatePath } );
        return engine.render( template, stash );
      }
    }
  }

  throw "Template not found";
}
<commit_msg>Make Context#absUrlFor pass url-like things through un-changed<commit_after>var juice = require('../juice'),
    getCaller = juice.Utils.getCaller,
    deprecated = juice.Utils.deprecationWarning,
    formatHTTPDate = juice.Utils.formatHTTPDate;

/**
 *  class juice.Context
 *
 *  Per request context. Is a 'poor-mans-subclass' of the application.
 **/
var Context = exports.Context = function Context(app, request) {

  function AppContext(request) {
    this.request = request;
  }
  AppContext.prototype = app;

  var ctx = new AppContext(request);

  // Now copy things from the Context.prototype to ctx object -- poor mans MI
  for (var i in Context.prototype) {
    if (Context.prototype.hasOwnProperty(i))
      ctx[i] = Context.prototype[i];
  }

  ctx.init( request );

  return ctx;

}

Context.prototype.init = function( ) {
  var ctx = this;

  var j_env = { docRoot: ctx.docRoot };
  if ("env" in ctx.request == false)
    ctx.request.env = { juice: j_env };
  else
    ctx.request.env.juice = j_env;

  ctx.hostUrl = ctx.request.scheme + "://";

  if ( ctx.request.headers.host )
    ctx.hostUrl += ctx.request.headers.host
  else {
    ctx.hostUrl += ctx.request.host;
    if ( ctx.request.port )
      ctx.hostUrl += ":" + ctx.request.port;
  }

  j_env.hostUrl = ctx.hostUrl;

  // Back compat for 0.1: warn in 0.2, remove in 0.3
  Object.defineProperty(this, "base", {
    getter: function() {
      deprecated("Context#base", "0.3");
      return ctx.hostUrl
    },
    setter: function( x ) {
      deprecated("Context#base", "0.3");
      return ctx.hostUrl = x;
    }
  } );

  Object.defineProperty(this, "redirectTo", {
    setter: function( x ) {
      deprecated("Context#redirectTo", "0.3");
      this.res.redirect( this.absUrlFor( x ), 302 );
    }
  } );

  ctx.response = new juice.Response(ctx);

  // Alias
  Object.defineProperty(this, "res", {
    getter: function() { return this.response },
    setter: function( x ) { return this.response = x }
  } );

  // Pull params into objects
  ctx.buildParams();
}

Context.prototype.runAction = function runAction(captures, action) {
  // decode parameters (i.e. "%20" -> " ")
  captures = captures.map( function( c ) decodeURIComponent( c ) );

  var data = action.action.apply(this, captures),
      res = this.response,
      redirect, template;

  // Make sure there is a merge funciton.
  if ( "merge" in res == false)
    res.merge = juice.Response.prototype.merge;

  if (action.raw) {
    res = data;
  }
  else if (res.file) {
    if (! res.headers[ 'last-modified' ]) {
      // Do it here rather than in merge to avoid a needless stat if its provided
      this.response.headers[ 'last-modified' ] = formatHTTPDate(
        filesystem.lastModified( res.file )
      );
    }

    // TODO: Do Server sniffing to make this work in more places
    res.headers[ 'x-sendfile' ] = res.file;
    res = res.merge( {
      status: 200,
      headers: {},
      body: []
    } );
  }
  else if (this.format == "json") {
    res = res.merge( {
      status: 200,
      headers: { contentType : 'application/json' },
      body: [ JSON.stringify( data ) ]
    } );
  }
  else if ( "status" in res == false && ( redirect = action.redirect ) ) {
    // If res.status is already set, dont redirect.
    res = res.merge( {
      status: 302,
      headers: { location: this.absUrlFor( redirect ) },
      body: []
    } );
  }
  else if (action.render) {
    data.juice = this.helpers;
    data.juice.urlFor = this.urlFor.bind(this);
    data.juice.abUrlFor = this.absUrlFor.bind(this);

    var rendered = this.renderTemplate( action.render, data );

    if ( rendered === undefined )
      throw "Couldn't find template file to load";

    res = res.merge( {
      status: 200,
      headers: { contentType : 'text/html' },
      body: [ rendered ]
    } );
  }
  else if ("status" in res == false || res.status < 300 && res.status > 399 ) {
    // Redirect actions dont need content
    throw new Error( "You need to specify a redirect or a template to render" );
  }
  else {
    res = res.merge( {
      body: [],
      headers: {}
    } );
  }

  return res;
}

Context.prototype.absUrlFor = function( url ) {
  // It already looks like an absolute url -- return it
  if ( arguments.length == 1 && /^\w+:/.exec( url ) )
    return url;

  return this.hostUrl + this.urlFor.apply(this, arguments);
}

Context.prototype.urlFor = function() {
  // TODO: Better url handling and deal with absolute arumgents.
  var url = Array.join(arguments, '/');
  url = url.replace(/\/+/g, '/')
           .replace(/^[^\/]/, '/');

  return this.request.scriptName + url;
}

Context.prototype.buildParams = function buildParams() {
  var store = this.request.env.juice;
  store.params = {
    get: this.request.queryString ? juice.Utils.splitQueryString(this.request.queryString) : {},
    post: {}
  };

  var len = this.request.headers['content-length'];
  if (len && this.request.method == "POST" &&
      this.request.headers['content-type'] == 'application/x-www-form-urlencoded')
  {
    var body = this.request.input.read(len).decodeToString();
    store.params.post = juice.Utils.splitQueryString(body);
  }

  this.params = store.params;
}

Context.prototype.renderTemplate = function( file, stash ) {
  // load templates config or default to TT if not available
  var conf = this.config.templates || { "tt" : "tt" };

  const fs = require( 'fs-base' );

  // check each include path in order
  for ( let i = 0; i < this.templatePath.length; ++i ) {
    // check each possible extension in order
    for ( let ext in conf ) {
      // build the path to the template being search for
      var path = this.templatePath[ i ] + file + '.' + ext;

      // if the template exists, render it with the right engine and return
      if ( fs.isFile( path ) ) {
        let template = fs.rawOpen( path, 'r' ).readWhole();

        // load the adaptor for the engine
        let Adaptor = this.loadRelativeComponent( "./view/" + conf[ ext ] ).View,
            engine = new Adaptor( { paths : this.templatePath } );
        return engine.render( template, stash );
      }
    }
  }

  throw "Template not found";
}
<|endoftext|>


<commit_before>'use babel'
/* globals atom */

import kd from 'kd.js'
import debug from 'debug'
import execa from 'execa'
import KD from './kd-states'
import LoadingView from './views/loading-view'
import PromptView from './views/prompt-view'
import TeamSelector from './views/team-selector'
import MachineSelector from './views/machine-selector'
import MountSelector from './views/mount-selector'
import getTeams from './utils/get-teams'
import getMachines from './utils/get-machines'
import getMounts from './utils/get-mounts'

const log = debug('kd-controller:log')
const error = debug('kd:err')

export default class KDController extends kd.Object {

  constructor (serializedState = {}) {
    super({}, serializedState)
    KD.init()

    this.modalPanel = null
  }

  showLoadingMessage (message, icon, duration) {

    this.modalPanel = atom.workspace.addModalPanel({
      className: 'kd-loading-modal',
      item: new LoadingView(message, icon),
      visible: true,
      priority: 201
    })

    if (duration) {
      setTimeout(() => this.modalPanel.destroy(), duration)
    }

    return this.modalPanel
  }

  showPrompt (prompts, callback) {
    let view = new PromptView(prompts)

    let panel = atom.workspace.addModalPanel({
      className: 'kd-loading-modal',
      item: view,
      visible: true,
      priority: 201
    })
    view.emitter.on('prompt-given', userInputs => {
      callback(null, userInputs)
    })
    view.emitter.on('prompt-cancelled', () => {
      callback(true)
    })
    return panel
  }

  _err (e) {

    if (this.modalPanel) {
      this.modalPanel.destroy()
      this.modalPanel = null
    }

    atom.notifications.addFatalError(`kd: ${e}`, {
      dismissable: true,
      stack: e.stack
    })

    error(e)
  }

  _showTreeView () {
    let activeEditor = atom.views.getView(atom.workspace.getActiveTextEditor())
    atom.commands.dispatch(activeEditor, 'tree-view:show')
  }

  _showSelector (items, selector) {
    selector.getItem().setItems(items)
    selector.getItem().attached()
    selector.show()
  }

  async showTeams () {
    log('showTeams')
    this.showLoadingMessage('Loading teams…')

    let teams
    try {
      teams = await getTeams()
      this.getData().teams = teams
    } catch (e) { return this._err(e) }

    if (this.teamSelector) {
      return this._showSelector(teams, this.teamSelector)
    }

    let view = new TeamSelector({ items: teams, label: 'team' })
    this.teamSelector = atom.workspace.addModalPanel({
      className: 'kd-team-selector',
      item: view,
      visible: true,
      priority: 201
    })
    view.emitter.on('teamSelector-cancelled', event => { this.teamSelector.hide() })
    view.emitter.on('teamSelector-confirmed', team => {
      this.getData().team = team
      this.showMachines(team.slug)
    })
  }

  async showMounts (showMessage = true) {
    log('showMounts')
    if (showMessage) {
      this.showLoadingMessage('Loading mounts…')
    }
    let mounts
    try {
      mounts = await getMounts()
      this.getData().mounts = mounts
    } catch (e) { return this._err(e) }

    if (this.mountSelector) {
      return this._showSelector(mounts, this.mountSelector)
    }

    let view = new MountSelector({ items: this.getData().mounts, label: 'mount' })
    this.mountSelector = atom.workspace.addModalPanel({
      className: 'kd-mount-selector',
      item: view,
      visible: true,
      priority: 201
    })
    view.emitter.on('mountSelector-cancelled', event => { this.mountSelector.hide() })
    view.emitter.on('mountSelector-confirmed', mount => {
      this.getData().mount = mount
      atom.workspace.project.addPath(mount.mount.path)
      this._showTreeView()
    })
  }

  async showMachines (team) {
    log('showMachines')
    this.showLoadingMessage('Loading machines…')

    let machines
    try {
      machines = await getMachines(team)
      this.getData().machines = machines
    } catch (e) { return this._err(e) }

    if (this.machineSelector) {
      return this._showSelector(machines, this.machineSelector)
    }

    let view = new MachineSelector({ items: machines, label: 'machine' })
    this.machineSelector = atom.workspace.addModalPanel({
      className: 'kd-machine-selector',
      item: view,
      visible: true,
      priority: 201
    })

    view.emitter.on('machineSelector-cancelled', event => { this.machineSelector.hide() })
    view.emitter.on('machineSelector-confirmed', machine => {
      this.getData().machine = machine

      const localBasePath = atom.config.get('kd.localBasePath')
      const localPath = `${localBasePath}/${machine.team}/${machine.label}`
      const remotePath = `/home/${machine.owner || machine.username}`

      let panel = this.showPrompt({
        prompts: [
          { name: 'localPath', title: 'Local path:', defaultValue: localPath, placeholder: localPath },
          { name: 'remotePath', title: 'Remote path:', defaultValue: remotePath, placeholder: remotePath }
        ],
        callback: (err, inputs) => {
          if (err) return panel.destroy()
          panel.destroy()
          let remoteAddress = `${machine.id}:${inputs.remotePath}`
          this.showLoadingMessage(`Mounting <code>${remoteAddress}</code> to <code>${inputs.localPath}</code>, please wait…`)
          execa('kd', ['machine', 'mount', remoteAddress, inputs.localPath]).then(result => {
            this.showLoadingMessage(`Machine mounted, preparing the mount please wait…`, 'check', 2000)
            atom.workspace.project.addPath(localPath)
            this._showTreeView()
          }).catch(e => {
            this.showLoadingMessage('Machine could not be mounted, rolling back…', 'x', 2000)
            error(e)
          })
        }
      })
    })
  }

}
<commit_msg>don't assume tree-view pkg is being used<commit_after>'use babel'
/* globals atom */

import kd from 'kd.js'
import debug from 'debug'
import execa from 'execa'
import KD from './kd-states'
import LoadingView from './views/loading-view'
import PromptView from './views/prompt-view'
import TeamSelector from './views/team-selector'
import MachineSelector from './views/machine-selector'
import MountSelector from './views/mount-selector'
import getTeams from './utils/get-teams'
import getMachines from './utils/get-machines'
import getMounts from './utils/get-mounts'

const log = debug('kd-controller:log')
const error = debug('kd:err')

export default class KDController extends kd.Object {

  constructor (serializedState = {}) {
    super({}, serializedState)
    KD.init()

    this.modalPanel = null
  }

  showLoadingMessage (message, icon, duration) {

    this.modalPanel = atom.workspace.addModalPanel({
      className: 'kd-loading-modal',
      item: new LoadingView(message, icon),
      visible: true,
      priority: 201
    })

    if (duration) {
      setTimeout(() => this.modalPanel.destroy(), duration)
    }

    return this.modalPanel
  }

  showPrompt (prompts, callback) {
    let view = new PromptView(prompts)

    let panel = atom.workspace.addModalPanel({
      className: 'kd-loading-modal',
      item: view,
      visible: true,
      priority: 201
    })
    view.emitter.on('prompt-given', userInputs => {
      callback(null, userInputs)
    })
    view.emitter.on('prompt-cancelled', () => {
      callback(true)
    })
    return panel
  }

  _err (e) {

    if (this.modalPanel) {
      this.modalPanel.destroy()
      this.modalPanel = null
    }

    atom.notifications.addFatalError(`kd: ${e}`, {
      dismissable: true,
      stack: e.stack
    })

    error(e)
  }

  _showTreeView () {
    try {
      let activeEditor = atom.views.getView(atom.workspace.getActiveTextEditor())
      atom.commands.dispatch(activeEditor, 'tree-view:show')
    } catch (_) {} // no tree view package is installed.
  }

  _showSelector (items, selector) {
    selector.getItem().setItems(items)
    selector.getItem().attached()
    selector.show()
  }

  async showTeams () {
    log('showTeams')
    this.showLoadingMessage('Loading teams…')

    let teams
    try {
      teams = await getTeams()
      this.getData().teams = teams
    } catch (e) { return this._err(e) }

    if (this.teamSelector) {
      return this._showSelector(teams, this.teamSelector)
    }

    let view = new TeamSelector({ items: teams, label: 'team' })
    this.teamSelector = atom.workspace.addModalPanel({
      className: 'kd-team-selector',
      item: view,
      visible: true,
      priority: 201
    })
    view.emitter.on('teamSelector-cancelled', event => { this.teamSelector.hide() })
    view.emitter.on('teamSelector-confirmed', team => {
      this.getData().team = team
      this.showMachines(team.slug)
    })
  }

  async showMounts (showMessage = true) {
    log('showMounts')
    if (showMessage) {
      this.showLoadingMessage('Loading mounts…')
    }
    let mounts
    try {
      mounts = await getMounts()
      this.getData().mounts = mounts
    } catch (e) { return this._err(e) }

    if (this.mountSelector) {
      return this._showSelector(mounts, this.mountSelector)
    }

    let view = new MountSelector({ items: this.getData().mounts, label: 'mount' })
    this.mountSelector = atom.workspace.addModalPanel({
      className: 'kd-mount-selector',
      item: view,
      visible: true,
      priority: 201
    })
    view.emitter.on('mountSelector-cancelled', event => { this.mountSelector.hide() })
    view.emitter.on('mountSelector-confirmed', mount => {
      this.getData().mount = mount
      atom.workspace.project.addPath(mount.mount.path)
      this._showTreeView()
    })
  }

  async showMachines (team) {
    log('showMachines')
    this.showLoadingMessage('Loading machines…')

    let machines
    try {
      machines = await getMachines(team)
      this.getData().machines = machines
    } catch (e) { return this._err(e) }

    if (this.machineSelector) {
      return this._showSelector(machines, this.machineSelector)
    }

    let view = new MachineSelector({ items: machines, label: 'machine' })
    this.machineSelector = atom.workspace.addModalPanel({
      className: 'kd-machine-selector',
      item: view,
      visible: true,
      priority: 201
    })

    view.emitter.on('machineSelector-cancelled', event => { this.machineSelector.hide() })
    view.emitter.on('machineSelector-confirmed', machine => {
      this.getData().machine = machine

      const localBasePath = atom.config.get('kd.localBasePath')
      const localPath = `${localBasePath}/${machine.team}/${machine.label}`
      const remotePath = `/home/${machine.owner || machine.username}`

      let panel = this.showPrompt({
        prompts: [
          { name: 'localPath', title: 'Local path:', defaultValue: localPath, placeholder: localPath },
          { name: 'remotePath', title: 'Remote path:', defaultValue: remotePath, placeholder: remotePath }
        ],
        callback: (err, inputs) => {
          if (err) return panel.destroy()
          panel.destroy()
          let remoteAddress = `${machine.id}:${inputs.remotePath}`
          this.showLoadingMessage(`Mounting <code>${remoteAddress}</code> to <code>${inputs.localPath}</code>, please wait…`)
          execa('kd', ['machine', 'mount', remoteAddress, inputs.localPath]).then(result => {
            this.showLoadingMessage(`Machine mounted, preparing the mount please wait…`, 'check', 2000)
            atom.workspace.project.addPath(localPath)
            this._showTreeView()
          }).catch(e => {
            this.showLoadingMessage('Machine could not be mounted, rolling back…', 'x', 2000)
            error(e)
          })
        }
      })
    })
  }

}
<|endoftext|>


<commit_before>var async = require('async'),
    lumbar = require('lumbar'),
    fu = lumbar.fileUtil;

function generator(string) {
  var ret = function(context, callback) { callback(undefined, {data: string, noSeparator: true}); };
  ret.sourceFile = undefined;
  return ret;
}

module.exports = function(options) {
  options = options || {};

  return {
    priority: 80,

    moduleResources: function(context, next, complete) {
      if (!options.includeTests && context.module['test-module']) {
        // Prevent output from any test modules if we aren't including tests
        return complete(undefined, []);
      }
      if (context.mode !== 'scripts' || context.module['test-module']) {
        // Don't touch anything outside of script mode or explicit test modules
        return next(complete);
      }

      var attr = context.config.attributes.test || {},
          includeDir = attr['auto-include'];

      next(function(err, ret) {
        if (err || !ret) {
          return complete(err);
        }

        // Only include the test output if the user explicitly opted in
        var tests = options.includeTests ? context.module.tests : [];

        // Auto-include if enabled
        if (!tests && includeDir) {
          tests = ret.map(function(resource) {
              var filterResource = (lumbar.build || context.config).filterResource;
              if (filterResource(resource, context)) {
                return includeDir + (resource.src || resource);
              }
            });
          async.filter(tests, function(resource, callback) {
              if (!resource) { return callback(false); }
              fu.stat(fu.resolvePath(resource), function(err) { callback(!err); });
            },
            finish);
        } else {
          finish(tests);
        }

        function finish(tests) {
          // Create a submodule scope for all of the output
          if (tests && tests.length) {
            ret.push(generator('exports.' + (attr.export || 'tests') + ' = function() {\n'));
            ret.push.apply(ret, tests);
            ret.push(generator('};\n'));
          }

          complete(undefined, ret);
        }
      });
    }
  };
};
<commit_msg>Add add directory monitoring for new test files<commit_after>var _ = require('underscore'),
    async = require('async'),
    lumbar = require('lumbar'),
    fu = lumbar.fileUtil,
    path = require('path');

function generator(string) {
  var ret = function(context, callback) { callback(undefined, {data: string, noSeparator: true}); };
  ret.sourceFile = undefined;
  return ret;
}

module.exports = function(options) {
  options = options || {};

  return {
    priority: 80,

    moduleResources: function(context, next, complete) {
      if (!options.includeTests && context.module['test-module']) {
        // Prevent output from any test modules if we aren't including tests
        return complete(undefined, []);
      }
      if (context.mode !== 'scripts' || context.module['test-module']) {
        // Don't touch anything outside of script mode or explicit test modules
        return next(complete);
      }

      var attr = context.config.attributes.test || {},
          includeDir = attr['auto-include'];

      next(function(err, ret) {
        if (err || !ret) {
          return complete(err);
        }

        // Only include the test output if the user explicitly opted in
        var tests = options.includeTests ? context.module.tests : [];

        // Auto-include if enabled
        if (!tests && includeDir) {
          tests = ret.map(function(resource) {
              var filterResource = (lumbar.build || context.config).filterResource;
              if (filterResource(resource, context)) {
                return includeDir + (resource.src || resource);
              }
            });
          async.map(tests, function(resource, callback) {
              if (!resource) { return callback(undefined, resource); }
              fu.stat(fu.resolvePath(resource), function(err) {
                if (!err) {
                  callback(undefined, resource);
                } else {
                  // File not found, watch the parent dir for adds
                  var dir = fu.resolvePath(path.dirname(resource));
                  fu.stat(dir, function(err) {
                    callback(undefined, !err && {watch: dir});
                  });
                }
              });
            },
            function(err, tests) {
              finish(_.filter(tests, function(test) { return test; }));
            });
        } else {
          finish(tests);
        }

        function finish(tests) {
          // Create a submodule scope for all of the output
          if (tests && tests.length) {
            ret.push(generator('exports.' + (attr.export || 'tests') + ' = function() {\n'));
            ret.push.apply(ret, tests);
            ret.push(generator('};\n'));
          }

          complete(undefined, ret);
        }
      });
    }
  };
};
<|endoftext|>


<commit_before>/* jshint node:true */

var commands = require('./commands/macro');

var macro = exports.macro = function(id) {

  var _id;
  var _flags = 0;
  var _extendedFlags = 0; //Currently unused (changes for Sphero 2.0?)
  var macroCommands = [];
  var size = 0;
  var _macro = {};

  if (typeof(id) === 'undefined') {
    _id = 0xFF; //temporary macro
  } else {
    _id = id;
  }

  _macro.id = function(id) {
    if (typeof(id) === 'undefined') {
      return _id;
    } else {
      _id = id;
      return _macro;
    }
  };

  _macro.flags = function(flags) {
    if (typeof(id) === 'undefined') {
      return _flags;
    } else {
      _flags = flags;
      return _macro;
    }
  };

  _macro.extendedFlags = function(extendedFlags) {
    if (typeof(id) === 'undefined') {
      return _extendedFlags;
    } else {
      _extendedFlags = flags;
      return _macro;
    }
  };

  _macro.append = function(buffer) {
    macroCommands.push(buffer);
    size = size+buffer.length;
    return _macro;
  };

  _macro.clear = function() {
    macroCommands = [];
    size = 0;
    return this;
  };

  _macro.done = function(noEnd) {
    var header;
    if (_flags && 0x80) {
      header = new Buffer(3);
      header.writeUInt8(_flags, 1);
    } else {
      header = new Buffer(2);
    }
    header.writeUInt8(_id, 0);
    header.writeUInt8(_flags, 1);

    var parts = [ header ].concat(macroCommands);
    if (!noEnd) {
      parts.push(new Buffer([0x00]));
    }

    var buffer = Buffer.concat(parts);
    return buffer;
  };

  _macro.size = function() {
    return size;
  };

  return _macro;

};
<commit_msg>Fix variable usage in macro-builder<commit_after>/* jshint node:true */

var commands = require('./commands/macro');

var macro = exports.macro = function(id) {

  var _id;
  var _flags = 0;
  var _extendedFlags = 0; //Currently unused (changes for Sphero 2.0?)
  var macroCommands = [];
  var size = 0;
  var _macro = {};

  if (typeof(id) === 'undefined') {
    _id = 0xFF; //temporary macro
  } else {
    _id = id;
  }

  _macro.id = function(id) {
    if (typeof(id) === 'undefined') {
      return _id;
    } else {
      _id = id;
      return _macro;
    }
  };

  _macro.flags = function(flags) {
    if (typeof(id) === 'undefined') {
      return _flags;
    } else {
      _flags = flags;
      return _macro;
    }
  };

  _macro.extendedFlags = function(extendedFlags) {
    if (typeof(id) === 'undefined') {
      return _extendedFlags;
    } else {
      _extendedFlags = extendedFlags;
      return _macro;
    }
  };

  _macro.append = function(buffer) {
    macroCommands.push(buffer);
    size = size+buffer.length;
    return _macro;
  };

  _macro.clear = function() {
    macroCommands = [];
    size = 0;
    return this;
  };

  _macro.done = function(noEnd) {
    var header;
    if (_flags && 0x80) {
      header = new Buffer(3);
      header.writeUInt8(_flags, 1);
    } else {
      header = new Buffer(2);
    }
    header.writeUInt8(_id, 0);
    header.writeUInt8(_flags, 1);

    var parts = [ header ].concat(macroCommands);
    if (!noEnd) {
      parts.push(new Buffer([0x00]));
    }

    var buffer = Buffer.concat(parts);
    return buffer;
  };

  _macro.size = function() {
    return size;
  };

  return _macro;

};
<|endoftext|>


<commit_before>const request = require('request').defaults({agent: false, pool: {maxSockets: 15},forever:true});
const hostname = require('os').hostname();
//const configStore = require('configstore');
const indiaUrl  = "http://10.4.0.50:8200/v1/post/metrics";
const ConfigStore = require('configstore');
const config = new ConfigStore("metrics").get("config");

let sendData = function(metric,tags,value){
    if(Object.keys(tags).length>0 && (process.env.NODE_ENV === "production" || process.env.NODE_ENV === "docker")  ){
        let metricData = {
            "metric": metric,
            "timestamp": new Date().getTime(),
            "value": value
        };
        metricData = Object.assign(tags, metricData);

        request({
            url:indiaUrl,
            method:"POST",
            json: metricData,
            timeout: 500
        },function(err){
            if(err) {
                console.log("druid error");
                console.log(err);
            }
        });

    } else if (Object.keys(tags).length>0 && process.env.NODE_ENV === "deployment" ) {

        let metricData = {
            "@timestamp": (new Date()).toISOString(),
            "value": value
        };

        if(metric && metric.indexOf('new-user') !== -1) {
            metric = "events.messages.count";
            metricData.newUser = 1;
        }

        metricData = Object.assign(tags, metricData);

        request({
            url:`${config.esUrl}/botmetrics-${metric}/_doc`,
            method:"POST",
            json: metricData,
            timeout: 500
        },function(err){
            if(err) {
                console.log(err);
            }
        });
    }
};

let serverStats =  function(options) {
    return function(req, res, next) {
        let startTime = new Date().getTime();
        let path = req.path;

        // Function called on response finish that sends stats to statsd
        function sendStats() {
            if (hostname.indexOf("local") !== -1) {
                cleanup();
                return;
            }
            let app = options.app;

            // Status Code
            let statusCode = res.statusCode || 'unknown_status';

            // Increment
            sendData("service-requests",1,{
                "app":app,
                "node": hostname,
                "status-code":statusCode,
                "path":path
            });

            // Response Time
            let duration = new Date().getTime() - startTime;
            // Duration
            sendData("service-response-time",duration,{
                "app":app,
                "node": hostname,
                "path":path
            });

            cleanup();
        }

        // Function to clean up the listeners we've added
        function cleanup() {
            res.removeListener('finish', sendStats);
            res.removeListener('error', cleanup);
            res.removeListener('close', cleanup);
        }

        // Add response listeners
        res.once('finish', sendStats);
        res.once('error', cleanup);
        res.once('close', cleanup);

        if (next) {
            next();
        }
    }
};

let increment =  function(metric,tags, value) {
    sendData(metric,tags,value||1);
};

module.exports =  {
    serverStats:serverStats,
    increment:increment
};
<commit_msg>Metrics druid added ..<commit_after>const request = require('request').defaults({agent: false, pool: {maxSockets: 15}, forever:true});
const hostname = require('os').hostname();
//const configStore = require('configstore');
const indiaUrl  = "http://10.4.0.50:8200/v1/post/metrics";
const ConfigStore = require('configstore');
const config = new ConfigStore("metrics").get("config");

let sendData = function(metric,tags,value){
    if(Object.keys(tags).length>0 && (process.env.NODE_ENV === "production" || process.env.NODE_ENV === "docker")  ){
        let metricData = {
            "metric": metric,
            "timestamp": new Date().getTime(),
            "value": value
        };
        metricData = Object.assign(tags, metricData);

        request({
            url:indiaUrl,
            method:"POST",
            json: metricData,
            timeout: 500
        },function(err){
            if(err) {
                console.log("druid error");
                console.log(err);
            }
        });

    } else if (Object.keys(tags).length>0 && process.env.NODE_ENV === "deployment" ) {

        let metricData = {
            "@timestamp": (new Date()).toISOString(),
            "value": value
        };

        if(metric && metric.indexOf('new-user') !== -1) {
            metric = "events.messages.count";
            metricData.newUser = 1;
        }

        metricData = Object.assign(tags, metricData);

        request({
            url:`${config.esUrl}/botmetrics-${metric}/_doc`,
            method:"POST",
            json: metricData,
            timeout: 500
        },function(err){
            if(err) {
                console.log(err);
            }
        });
    }
};

let serverStats =  function(options) {
    return function(req, res, next) {
        let startTime = new Date().getTime();
        let path = req.path;

        // Function called on response finish that sends stats to statsd
        function sendStats() {
            if (hostname.indexOf("local") !== -1) {
                cleanup();
                return;
            }
            let app = options.app;

            // Status Code
            let statusCode = res.statusCode || 'unknown_status';

            // Increment
            sendData("service-requests",1,{
                "app":app,
                "node": hostname,
                "status-code":statusCode,
                "path":path
            });

            // Response Time
            let duration = new Date().getTime() - startTime;
            // Duration
            sendData("service-response-time",duration,{
                "app":app,
                "node": hostname,
                "path":path
            });

            cleanup();
        }

        // Function to clean up the listeners we've added
        function cleanup() {
            res.removeListener('finish', sendStats);
            res.removeListener('error', cleanup);
            res.removeListener('close', cleanup);
        }

        // Add response listeners
        res.once('finish', sendStats);
        res.once('error', cleanup);
        res.once('close', cleanup);

        if (next) {
            next();
        }
    }
};

let increment =  function(metric,tags, value) {
    sendData(metric,tags,value||1);
};

module.exports =  {
    serverStats:serverStats,
    increment:increment
};
<|endoftext|>


<commit_before>/** internal
 *  class Server
 *
 *  Easy to use server/middleware ideal for serving assets your assets:
 *
 *  - great for development, as it recompiles canged assets on-fly
 *  - great for production, as it caches results, and it can become as effecient
 *    as `staticCache` middleware (or even better) of `connect` module.
 *
 *
 *  ##### Examples
 *
 *      // development mode
 *      var srv = new Server(env);
 *
 *      // production mode
 *      var srv = new Server(env.index);
 *
 *      // production mode (restrictive)
 *      var files = ['app.js', 'app.css', 'logo.jpg'];
 *      manifest.compile(files, function (err, manifestData) {
 *        var srv = new Server(env.index, manifestData);
 *      });
 **/

'use strict';

// stdlib
var http = require('http');
var zlib = require('zlib');
var url  = require('url');

// 3rd-party
var async = require('async');

// internal
var logger      = require('./logger');
var getter      = require('./common').getter;
var prop        = require('./common').prop;
var start_timer = require('./common').timer;

////////////////////////////////////////////////////////////////////////////////

/**
 *  new Server(environment[, manifest])
 *  - environment (Environment|Index)
 *  - manifest (Object): Data returned by [[Manifest#compile]]
 *
 *  If you provide `manifest`, then server will not even try to find files on
 *  FS unless they are specified in the `manifest`.
 **/
var Server = module.exports = function Server(environment, manifest) {
  prop(this, 'environment', environment);
  prop(this, 'manifest',    manifest);
};

// Retruns fingerprint from the pathname
var FINGERPRINT_RE = /-([0-9a-f]{32,40})\.[^.]+$/;
function get_fingerprint(pathname) {
  var m = FINGERPRINT_RE.exec(pathname);
  return m ? m[1] : null;
}

// Helper to write the code and end response
function end(res, code) {
  res.writeHead(code);
  res.end();
}

// Returns Etag value for `asset`
function etag(asset) {
  return '"' + asset.digest + '"';
}

// Returns true whenever If-None-Match header matches etag of `asset`
function is_etag_match(req, asset) {
  return etag(asset) === req.headers['if-none-match'];
}

// Returns true whenever If-Modified-Since header matches mtime of `asset`
function is_not_modified(req, asset) {
  var mtime = Date.parse(req.headers['if-modified-since']);

  // can't parse header's value
  if (isNaN(mtime)) {
    return false;
  }

  // if-modified-since >= asset's modification time
  return asset.mtime.getTime() <= mtime;
}

// Returns whenever an asset can be gzipped or not
var COMPRESSABLE_MIME_RE = new RegExp([
  '^text/',
  '/json$',
  '/javascript$',
  '/svg+xml$',
  '/x-font-ttf$',
  '/x-font-otf$'
].join('|'));
function is_compressable(asset) {
  return COMPRESSABLE_MIME_RE.test(asset.contentType);
}

// Tells whenever browser accepts gzip at all
function is_gzip_accepted(req) {
  var accept = req.headers['accept-encoding'] || '';
  return '*' === accept || 0 <= accept.indexOf('gzip');
}

// Tells whenever browser accepts gzip at all
function is_deflate_accepted(req) {
  var accept = req.headers['accept-encoding'] || '';
  return 0 <= accept.indexOf('deflate');
}

// small helper to set cmpressed buffer cache on asset.
// compressor is executed ONLY if asset has no cached property yet.
function set_compressed_cache(compressor, asset, name, callback) {
  if (undefined !== asset[name]) {
    callback();
    return;
  }

  if (500 > asset.length) {
    // asset is too small - compression will not give any profit.
    // mark asset as non-compressable.
    prop(asset, name, false);
    callback();
    return;
  }

  compressor(asset.buffer, function (err, buffer) {
    if (!err) {
      // asign property only on success
      prop(asset, name, buffer);
    }
    callback(err);
  });
}

/**
 *  Server#compile(pathname, callback(err, asset)) -> Void
 *  - pathname (String)
 *  - callback (Function)
 *
 *  Finds and compiles given asset.
 **/
Server.prototype.compile = function (pathname, callback) {
  var asset = (this.manifest && !this.manifest.assets[pathname]) ? null
            : this.environment.findAsset(pathname, {bundle: true});

  if (!asset) {
    callback(/* err = undefined, asset = undefined */);
    return;
  }

  asset.compile(function (err/*, asset */) {
    if (err) {
      callback(err/*, asset = undefined */);
      return;
    }

    // got static asset without cached buffer - cache it
    if ('static' === asset.type && undefined === asset.__buffer__) {
      prop(asset, '__buffer__', asset.buffer);
    }

    // Cache gzipped buffer if asset is gzipable
    if (is_compressable(asset) && (undefined === asset.gzipped || undefined === asset.deflated)) {
      async.series([
        async.apply(set_compressed_cache, zlib.gzip, asset, 'gzipped'),
        async.apply(set_compressed_cache, zlib.deflate, asset, 'deflated')
      ], function (err) {
        callback(err, asset);
      });
      return;
    }

    // pass back to servant
    callback(err, asset);
  });
};

/**
 *  Server#handle(req, res) -> Void
 *  - req (http.ServerRequest)
 *  - res (hhtp.ServerResponse)
 *
 *  Hander function suitable for usage as server `request` event listenet or as
 *  middleware for TJ's `connect` module.
 **/
Server.prototype.handle = function (req, res) {
  var timer       = start_timer(),
      pathname    = url.parse(req.url).pathname,
      msg_prefix  = 'Served asset ' + req.url + ' - ',
      fingerprint = get_fingerprint(pathname);

  try {
    pathname = decodeURIComponent(pathname.replace(/^\//, ''));
  } catch (err) {
    logger.error(msg_prefix + 'Failed decode URL');
    end(res, 400);
    return;
  }

  // forbid requests with `..` or NUL chars
  if (0 <= pathname.indexOf('..') || 0 <= pathname.indexOf("\u0000")) {
    logger.error(msg_prefix + 'URL contains unsafe chars');
    end(res, 403);
    return;
  }

  // ignore non-GET requests
  if ('GET' !== req.method && 'HEAD' !== req.method) {
    logger.error(msg_prefix + "HTTP method '" + req.method + "' not allowed");
    end(res, 403);
    return;
  }

  // remove fingerprint (digest) from URL
  if (fingerprint) {
    pathname = pathname.replace('-' + fingerprint, '');
  }

  // try to find and compile asset
  this.compile(pathname, function (err, asset) {
    var buffer, length;

    if (err) {
      logger.error(msg_prefix + "Error compiling asset " + pathname);
      logger.error(msg_prefix + (err.message || err.toString()));
      logger.info(msg_prefix + "500 Application Error" + timer.stop());
      end(res, 500);
      return;
    }

    // asset not found
    if (!asset) {
      logger.info(msg_prefix + "404 Not found" + timer.stop());
      end(res, 404);
      return;
    }

    //
    // Asset found. Sending headers for 200/304 responses:
    // http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-18#section-4.1
    //

    //
    // Ranges are not supported yet
    //

    res.removeHeader('Accept-Ranges');

    //
    // Mark for proxies, that we can return different content (plain & gzipped),
    // depending on specified (comma-separated) headers
    //

    res.setHeader('Vary', 'Accept-Encoding');

    //
    // Set caching headers
    //

    if (fingerprint) {
      // If the request url contains a fingerprint, set a long
      // expires on the response
      res.setHeader('Cache-Control', 'public, max-age=31536000');
    } else {
      // Otherwise set `must-revalidate` since the asset could be modified.
      res.setHeader('Cache-Control', 'public, must-revalidate');
    }

    res.setHeader('Date',           (new Date()).toUTCString());
    res.setHeader('Last-Modified',  asset.mtime.toUTCString());
    res.setHeader('ETag',           etag(asset));
    res.setHeader('Server',         'Nokla 1630');

    //
    // Check if asset's etag matches `if-none-match` header
    //

    if (is_etag_match(req, asset)) {
      logger.info(msg_prefix + "304 Not Modified" + timer.stop());
      end(res, 304);
      return;
    }

    //
    // Check modification time ONLY when `if-none-match` was not presented:
    // http://stackoverflow.com/questions/2021882/is-my-implementation-of-http-conditional-get-answers-in-php-is-ok/2049723#2049723
    //

    if (!req.headers['if-none-match'] && is_not_modified(req, asset)) {
      logger.info(msg_prefix + "304 Not Modified" + timer.stop());
      end(res, 304);
      return;
    }

    // OK
    logger.info(msg_prefix + "200 OK" + timer.stop());

    //
    // Alter some headers for gzipped assets
    //

    if (asset.deflated && is_deflate_accepted(req)) {
      buffer = asset.deflated;
      length = asset.deflated.length;
      res.setHeader('Content-Encoding', 'deflate');
    }

    // gzip takes precedence if available

    if (asset.gzipped && is_gzip_accepted(req)) {
      buffer = asset.gzipped;
      length = asset.gzipped.length;
      res.setHeader('Content-Encoding', 'gzip');
    }

    //
    // Set content type and length headers
    //

    res.setHeader('Content-Type',   asset.contentType);
    res.setHeader('Content-Length', length || asset.length);

    res.statusCode = 200;

    if ('HEAD' === req.method) {
      res.end();
      return;
    }

    res.end(buffer || asset.buffer);
  });
};

/**
 *  Server.createServer(environment[, manifest]) -> Function
 *  - environment (Environment)
 *  - manifest (Object)
 *
 *  Returns a server function suitable to be used as `request` event handler of
 *  `http` Node library module or as `connect` middleware.
 *
 *
 *  ##### Example
 *
 *      // Using TJ's Connect module
 *      var app = connect();
 *      app.use('/assets/', Server.createServer(env));
 *
 *
 *  ##### See Also
 *
 *  - [[Server.new]]
 **/
Server.createServer = function (environment, manifest) {
  var srv = new Server(environment, manifest);
  return function (req, res) {
    return srv.handle(req, res);
  };
};
<commit_msg>Do not check if-modified-since header<commit_after>/** internal
 *  class Server
 *
 *  Easy to use server/middleware ideal for serving assets your assets:
 *
 *  - great for development, as it recompiles canged assets on-fly
 *  - great for production, as it caches results, and it can become as effecient
 *    as `staticCache` middleware (or even better) of `connect` module.
 *
 *
 *  ##### Examples
 *
 *      // development mode
 *      var srv = new Server(env);
 *
 *      // production mode
 *      var srv = new Server(env.index);
 *
 *      // production mode (restrictive)
 *      var files = ['app.js', 'app.css', 'logo.jpg'];
 *      manifest.compile(files, function (err, manifestData) {
 *        var srv = new Server(env.index, manifestData);
 *      });
 **/

'use strict';

// stdlib
var http = require('http');
var zlib = require('zlib');
var url  = require('url');

// 3rd-party
var async = require('async');

// internal
var logger      = require('./logger');
var getter      = require('./common').getter;
var prop        = require('./common').prop;
var start_timer = require('./common').timer;

////////////////////////////////////////////////////////////////////////////////

/**
 *  new Server(environment[, manifest])
 *  - environment (Environment|Index)
 *  - manifest (Object): Data returned by [[Manifest#compile]]
 *
 *  If you provide `manifest`, then server will not even try to find files on
 *  FS unless they are specified in the `manifest`.
 **/
var Server = module.exports = function Server(environment, manifest) {
  prop(this, 'environment', environment);
  prop(this, 'manifest',    manifest);
};

// Retruns fingerprint from the pathname
var FINGERPRINT_RE = /-([0-9a-f]{32,40})\.[^.]+$/;
function get_fingerprint(pathname) {
  var m = FINGERPRINT_RE.exec(pathname);
  return m ? m[1] : null;
}

// Helper to write the code and end response
function end(res, code) {
  res.writeHead(code);
  res.end();
}

// Returns Etag value for `asset`
function etag(asset) {
  return '"' + asset.digest + '"';
}

// Returns true whenever If-None-Match header matches etag of `asset`
function is_etag_match(req, asset) {
  return etag(asset) === req.headers['if-none-match'];
}

// Returns whenever an asset can be gzipped or not
var COMPRESSABLE_MIME_RE = new RegExp([
  '^text/',
  '/json$',
  '/javascript$',
  '/svg+xml$',
  '/x-font-ttf$',
  '/x-font-otf$'
].join('|'));
function is_compressable(asset) {
  return COMPRESSABLE_MIME_RE.test(asset.contentType);
}

// Tells whenever browser accepts gzip at all
function is_gzip_accepted(req) {
  var accept = req.headers['accept-encoding'] || '';
  return '*' === accept || 0 <= accept.indexOf('gzip');
}

// Tells whenever browser accepts gzip at all
function is_deflate_accepted(req) {
  var accept = req.headers['accept-encoding'] || '';
  return 0 <= accept.indexOf('deflate');
}

// small helper to set cmpressed buffer cache on asset.
// compressor is executed ONLY if asset has no cached property yet.
function set_compressed_cache(compressor, asset, name, callback) {
  if (undefined !== asset[name]) {
    callback();
    return;
  }

  if (500 > asset.length) {
    // asset is too small - compression will not give any profit.
    // mark asset as non-compressable.
    prop(asset, name, false);
    callback();
    return;
  }

  compressor(asset.buffer, function (err, buffer) {
    if (!err) {
      // asign property only on success
      prop(asset, name, buffer);
    }
    callback(err);
  });
}

/**
 *  Server#compile(pathname, callback(err, asset)) -> Void
 *  - pathname (String)
 *  - callback (Function)
 *
 *  Finds and compiles given asset.
 **/
Server.prototype.compile = function (pathname, callback) {
  var asset = (this.manifest && !this.manifest.assets[pathname]) ? null
            : this.environment.findAsset(pathname, {bundle: true});

  if (!asset) {
    callback(/* err = undefined, asset = undefined */);
    return;
  }

  asset.compile(function (err/*, asset */) {
    if (err) {
      callback(err/*, asset = undefined */);
      return;
    }

    // got static asset without cached buffer - cache it
    if ('static' === asset.type && undefined === asset.__buffer__) {
      prop(asset, '__buffer__', asset.buffer);
    }

    // Cache gzipped buffer if asset is gzipable
    if (is_compressable(asset) && (undefined === asset.gzipped || undefined === asset.deflated)) {
      async.series([
        async.apply(set_compressed_cache, zlib.gzip, asset, 'gzipped'),
        async.apply(set_compressed_cache, zlib.deflate, asset, 'deflated')
      ], function (err) {
        callback(err, asset);
      });
      return;
    }

    // pass back to servant
    callback(err, asset);
  });
};

/**
 *  Server#handle(req, res) -> Void
 *  - req (http.ServerRequest)
 *  - res (hhtp.ServerResponse)
 *
 *  Hander function suitable for usage as server `request` event listenet or as
 *  middleware for TJ's `connect` module.
 **/
Server.prototype.handle = function (req, res) {
  var timer       = start_timer(),
      pathname    = url.parse(req.url).pathname,
      msg_prefix  = 'Served asset ' + req.url + ' - ',
      fingerprint = get_fingerprint(pathname);

  try {
    pathname = decodeURIComponent(pathname.replace(/^\//, ''));
  } catch (err) {
    logger.error(msg_prefix + 'Failed decode URL');
    end(res, 400);
    return;
  }

  // forbid requests with `..` or NUL chars
  if (0 <= pathname.indexOf('..') || 0 <= pathname.indexOf("\u0000")) {
    logger.error(msg_prefix + 'URL contains unsafe chars');
    end(res, 403);
    return;
  }

  // ignore non-GET requests
  if ('GET' !== req.method && 'HEAD' !== req.method) {
    logger.error(msg_prefix + "HTTP method '" + req.method + "' not allowed");
    end(res, 403);
    return;
  }

  // remove fingerprint (digest) from URL
  if (fingerprint) {
    pathname = pathname.replace('-' + fingerprint, '');
  }

  // try to find and compile asset
  this.compile(pathname, function (err, asset) {
    var buffer, length;

    if (err) {
      logger.error(msg_prefix + "Error compiling asset " + pathname);
      logger.error(msg_prefix + (err.message || err.toString()));
      logger.info(msg_prefix + "500 Application Error" + timer.stop());
      end(res, 500);
      return;
    }

    // asset not found
    if (!asset) {
      logger.info(msg_prefix + "404 Not found" + timer.stop());
      end(res, 404);
      return;
    }

    //
    // Asset found. Sending headers for 200/304 responses:
    // http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-18#section-4.1
    //

    //
    // Ranges are not supported yet
    //

    res.removeHeader('Accept-Ranges');

    //
    // Mark for proxies, that we can return different content (plain & gzipped),
    // depending on specified (comma-separated) headers
    //

    res.setHeader('Vary', 'Accept-Encoding');

    //
    // Set caching headers
    //

    if (fingerprint) {
      // If the request url contains a fingerprint, set a long
      // expires on the response
      res.setHeader('Cache-Control', 'public, max-age=31536000');
    } else {
      // Otherwise set `must-revalidate` since the asset could be modified.
      res.setHeader('Cache-Control', 'public, must-revalidate');
    }

    res.setHeader('Date',           (new Date()).toUTCString());
    res.setHeader('Last-Modified',  asset.mtime.toUTCString());
    res.setHeader('ETag',           etag(asset));
    res.setHeader('Server',         'Nokla 1630');

    //
    // Check if asset's etag matches `if-none-match` header
    //

    if (is_etag_match(req, asset)) {
      logger.info(msg_prefix + "304 Not Modified" + timer.stop());
      end(res, 304);
      return;
    }

    // OK
    logger.info(msg_prefix + "200 OK" + timer.stop());

    //
    // Alter some headers for gzipped assets
    //

    if (asset.deflated && is_deflate_accepted(req)) {
      buffer = asset.deflated;
      length = asset.deflated.length;
      res.setHeader('Content-Encoding', 'deflate');
    }

    // gzip takes precedence if available

    if (asset.gzipped && is_gzip_accepted(req)) {
      buffer = asset.gzipped;
      length = asset.gzipped.length;
      res.setHeader('Content-Encoding', 'gzip');
    }

    //
    // Set content type and length headers
    //

    res.setHeader('Content-Type',   asset.contentType);
    res.setHeader('Content-Length', length || asset.length);

    res.statusCode = 200;

    if ('HEAD' === req.method) {
      res.end();
      return;
    }

    res.end(buffer || asset.buffer);
  });
};

/**
 *  Server.createServer(environment[, manifest]) -> Function
 *  - environment (Environment)
 *  - manifest (Object)
 *
 *  Returns a server function suitable to be used as `request` event handler of
 *  `http` Node library module or as `connect` middleware.
 *
 *
 *  ##### Example
 *
 *      // Using TJ's Connect module
 *      var app = connect();
 *      app.use('/assets/', Server.createServer(env));
 *
 *
 *  ##### See Also
 *
 *  - [[Server.new]]
 **/
Server.createServer = function (environment, manifest) {
  var srv = new Server(environment, manifest);
  return function (req, res) {
    return srv.handle(req, res);
  };
};
<|endoftext|>


<commit_before>var validateOne = function(validatorFunction, fieldName) {
  // Get field value.
  var value = this.get(fieldName);

  try {
    // Run validator function which throws exception which we have to catch and
    // add error message to appropriate field.
    validatorFunction.call(this, value, fieldName);

    return true;
  } catch (e) {
    if (e instanceof ValidationError) {
      this._errors.set(fieldName, e.message);
    } else {
      throw e;
    }

    return false;
  }
};

var validate = function(fieldName) {
  // Functions "schema.getValidator" and "schema.getValidators" get validation
  // data defined in the given class schema not global validator object.

  // Get schema for given object instance.
  var schema = this.constructor.schema;

  // At first clear list of all error messages.
  var validators = schema.getValidators();
  _.each(validators, function(validator, fieldName) {
    this._errors.set(fieldName, undefined);
  }, this);

  var isValid = true;
  if (fieldName) {
    // Get validation function for given field name.
    var validatorFunction = schema.getValidator(fieldName);

    // Validate field only if there is validation function.
    if (validatorFunction) {
      isValid = validateOne.call(this, validatorFunction, fieldName);
    }
  } else {
    // Get schema validators and run each one.
    isValid = _.all(validators, validateOne, this);
  }

  // Set reactive object's error indicator.
  this._hasError.set(!isValid);

  return isValid;
};

var getError = function(fieldName) {
  return this._errors.get(fieldName);
};

var hasError = function() {
  return this._hasError.get();
};

var afterSet = function() {
  this._hasError.set(false);
};

Astro.Module({
  name: 'Validators',
  initSchema: function(Class, definition) {
    this._validators = {};

    if (_.has(definition, 'validators')) {
      this.addValidators(definition.validators);
    }

    this.addEvent('afterSet', afterSet);

    Class.prototype.validate = validate;
    Class.prototype.getError = getError;
    Class.prototype.hasError = hasError;
  },
  initInstance: function(attrs) {
    this._errors = new ReactiveDict();
    this._hasError = new ReactiveVar(false);
  }
});
<commit_msg>Fix hasError to have correct value<commit_after>var validateOne = function(validatorFunction, fieldName) {
  // Get field value.
  var value = this.get(fieldName);

  try {
    // Run validator function which throws exception which we have to catch and
    // add error message to appropriate field.
    validatorFunction.call(this, value, fieldName);

    // If a field's validation didn't throw an exception then clear an error
    // message for the given field.
    this._errors.set(fieldName, undefined);

    return true;
  } catch (e) {
    if (e instanceof ValidationError) {
      this._errors.set(fieldName, e.message);
    } else {
      throw e;
    }

    return false;
  }
};

var validate = function(fieldName) {
  // Functions "schema.getValidator" and "schema.getValidators" get validation
  // data defined in the given class schema not global validator object.

  // Get schema for given object instance.
  var schema = this.constructor.schema;

  // Get list all of all validators.
  var validators = schema.getValidators();

  var isValid = true;
  if (fieldName) {
    // Get validation function for given field name.
    var validatorFunction = validators[fieldName];

    // Validate field only if there is validation function.
    if (validatorFunction) {
      isValid = validateOne.call(this, validatorFunction, fieldName);
    }

    // When the given field passed validation, we have to check how many errors
    // we still have and decide whether the "_hasErrors" reactive var should be
    // set.
    this._hasError.set(
      !_.isUndefined(
        _.find(validators, function(validator, fieldName) {
          return this._errors.get(fieldName);
        }, this)
      )
    );
  } else {
    // Get schema validators and run each one.
    isValid = _.all(validators, validateOne, this);

    // Set reactive object's error indicator.
    this._hasError.set(!isValid);
  }

  return isValid;
};

var getError = function(fieldName) {
  return this._errors.get(fieldName);
};

var hasError = function() {
  return this._hasError.get();
};

var afterSet = function() {
  this._hasError.set(false);
};

Astro.Module({
  name: 'Validators',
  initSchema: function(Class, definition) {
    this._validators = {};

    if (_.has(definition, 'validators')) {
      this.addValidators(definition.validators);
    }

    this.addEvent('afterSet', afterSet);

    Class.prototype.validate = validate;
    Class.prototype.getError = getError;
    Class.prototype.hasError = hasError;
  },
  initInstance: function(attrs) {
    this._errors = new ReactiveDict();
    this._hasError = new ReactiveVar(false);
  }
});
<|endoftext|>


<commit_before>var bot = require('../utils/bot');
module.exports = {};
exports = module.exports;
queue = [];
const ytdl = require('ytdl-core');
const streamOptions = {
    seek: 0,
    volume: 1
};
var stopped = false;
var inform_np = true;

var now_playing_data = {};
var queue = [];
var voice_connection = null;
var voice_handler = null;
var voice_channel = null;
var text_channel = null;
exports.play = function (URL, message) {
    member = message.member;
    if (!url.includes("http")) return;
    console.log("QUEUE size" + queue.length);
    voice_channel = member.voiceChannel;
    text_channel = message.channel;
    if (voice_channel == undefined) {
        member.guild.channels.find(function (channel) {
            if (channel.type === 'text' && channel.name === "music-lobby") return channel;
        }).sendMessage("Join the Music-Lobby");

    } else if (voice_channel.name != "Music-Lobby") {
        voice_channel = member.guild.channels.find(function (channel) {
            if (channel.type === 'voice' && channel.name === "Music-Lobby") return channel;
        });
        member.setVoiceChannel(voice_channel);
    }
    if (queue.length === 0) {
        voice_channel.join().then(connection => {
            voice_connection = connection;
            add_to_queue(url, message);
        }).catch(console.error);
    } else {
        add_to_queue(url, message);
    }
}
exports.playNext = function (message) {
    if (voice_handler !== null) {
        message.reply("Skipping...");
        voice_handler.end();
    } else {
        play_next_song();
    }
}
exports.clear = function (message) {
    if (!message.member.hasPermission("MOVE_MEMBERS")) {
        message.reply("Not Worthy");
        return;
    }
    if (stopped) {
        message.reply("Playback is already stopped!");
    } else {
        if (voice_handler !== null) {
            voice_handler.end();
            bot.voiceConnections.find((connection) => {
                if (connection.channel.guild.id === message.guild.id)
                    return connection;
            }).disconnect();
            queue = [];
        }
        stopped = true;
        message.reply("Stopping!");
    }
}
exports.pause = function (message) {
    if (stopped) {
        message.reply("Playback is already stopped!");
    } else {
        stopped = true;
        if (voice_handler !== null) {
            voice_handler.end();
        }
        message.reply("Stopping!");
    }
}
exports.resume = function (message) {
    if (stopped) {
        stopped = false;
        if (!is_queue_empty()) {
            play_next_song();
        }
    } else {
        message.reply("Playback is already running");
    }
}
exports.setVol = function (vol) {
    if (vol > 1 || vol < 0)
        return "Invalid Volume. Volume Range(0-1)";
    voice_handler.setVolume(vol);
    return "Volume Has been set"
}
exports.queue = function (message) {
    var response = "";

    if (is_queue_empty()) {
        response = "the queue is empty.";
    } else {
        for (var i = 0; i < queue.length; i++) {
            response += "<" + queue[i]["video"] + "> (requested by " + queue[i]["user"] + ")\n";
        }
    }
    message.reply(response);
}

function add_to_queue(url, message) {
    queue.push({
        video: url,
        user: message.author.username
    });
    message.reply('added to the queue.');
    if (!stopped && !is_bot_playing() && queue.length === 1) {
        play_next_song();
    }
}

function play_next_song() {
    if (queue.length == 0) {
        text_channel.sendMessage("Queue Empty");
        return "Queue Empty";
    }
    var url = queue[0]["video"];
    var user = queue[0]["user"];

    now_playing_data["video"] = url;
    now_playing_data["user"] = user;

    if (inform_np) {
        text_channel.sendMessage('Now playing: <' + url + '> (requested by ' + user + ')');
    }

    var audio_stream = ytdl(url);
    voice_handler = voice_connection.playStream(audio_stream);

    voice_handler.once("end", reason => {
        voice_handler = null;
        if (!stopped && !is_queue_empty()) {
            play_next_song();
        }
    });

    queue.splice(0, 1);
}

function is_queue_empty() {
    return queue.length === 0;
}

function is_bot_playing() {
    return voice_handler !== null;
}
<commit_msg>Clear queue in any case<commit_after>var bot = require('../utils/bot');
module.exports = {};
exports = module.exports;
queue = [];
const ytdl = require('ytdl-core');
const streamOptions = {
    seek: 0,
    volume: 1
};
var stopped = false;
var inform_np = true;

var now_playing_data = {};
var queue = [];
var voice_connection = null;
var voice_handler = null;
var voice_channel = null;
var text_channel = null;
exports.play = function (URL, message) {
    member = message.member;
    if (!url.includes("http")) return;
    console.log("QUEUE size" + queue.length);
    voice_channel = member.voiceChannel;
    text_channel = message.channel;
    if (voice_channel == undefined) {
        member.guild.channels.find(function (channel) {
            if (channel.type === 'text' && channel.name === "music-lobby") return channel;
        }).sendMessage("Join the Music-Lobby");

    } else if (voice_channel.name != "Music-Lobby") {
        voice_channel = member.guild.channels.find(function (channel) {
            if (channel.type === 'voice' && channel.name === "Music-Lobby") return channel;
        });
        member.setVoiceChannel(voice_channel);
    }
    if (queue.length === 0) {
        voice_channel.join().then(connection => {
            voice_connection = connection;
            add_to_queue(url, message);
        }).catch(console.error);
    } else {
        add_to_queue(url, message);
    }
}
exports.playNext = function (message) {
    if (voice_handler !== null) {
        message.reply("Skipping...");
        voice_handler.end();
    } else {
        play_next_song();
    }
}
exports.clear = function (message) {
    if (!message.member.hasPermission("MOVE_MEMBERS")) {
        message.reply("Not Worthy");
        return;
    }
    queue = [];
    if (stopped) {
        message.reply("Playback is already stopped!");
    } else {
        if (voice_handler !== null) {
            voice_handler.end();
            bot.voiceConnections.find((connection) => {
                if (connection.channel.guild.id === message.guild.id)
                    return connection;
            }).disconnect();
        }
        stopped = true;
        message.reply("Stopping!");
    }
}
exports.pause = function (message) {
    if (stopped) {
        message.reply("Playback is already stopped!");
    } else {
        stopped = true;
        if (voice_handler !== null) {
            voice_handler.end();
        }
        message.reply("Stopping!");
    }
}
exports.resume = function (message) {
    if (stopped) {
        stopped = false;
        if (!is_queue_empty()) {
            play_next_song();
        }
    } else {
        message.reply("Playback is already running");
    }
}
exports.setVol = function (vol) {
    if (vol > 1 || vol < 0)
        return "Invalid Volume. Volume Range(0-1)";
    voice_handler.setVolume(vol);
    return "Volume Has been set"
}
exports.queue = function (message) {
    var response = "";

    if (is_queue_empty()) {
        response = "the queue is empty.";
    } else {
        for (var i = 0; i < queue.length; i++) {
            response += "<" + queue[i]["video"] + "> (requested by " + queue[i]["user"] + ")\n";
        }
    }
    message.reply(response);
}

function add_to_queue(url, message) {
    queue.push({
        video: url,
        user: message.author.username
    });
    message.reply('added to the queue.');
    if (!stopped && !is_bot_playing() && queue.length === 1) {
        play_next_song();
    }
}

function play_next_song() {
    if (queue.length == 0) {
        text_channel.sendMessage("Queue Empty");
        return "Queue Empty";
    }
    var url = queue[0]["video"];
    var user = queue[0]["user"];

    now_playing_data["video"] = url;
    now_playing_data["user"] = user;

    if (inform_np) {
        text_channel.sendMessage('Now playing: <' + url + '> (requested by ' + user + ')');
    }

    var audio_stream = ytdl(url);
    voice_handler = voice_connection.playStream(audio_stream);

    voice_handler.once("end", reason => {
        voice_handler = null;
        if (!stopped && !is_queue_empty()) {
            play_next_song();
        }
    });

    queue.splice(0, 1);
}

function is_queue_empty() {
    return queue.length === 0;
}

function is_bot_playing() {
    return voice_handler !== null;
}
<|endoftext|>


<commit_before>/*!
 * node-progress
 * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Expose `ProgressBar`.
 */

exports = module.exports = ProgressBar;

/**
 * Initialize a `ProgressBar` with the given `fmt` string and `options` or
 * `total`.
 *
 * Options:
 *
 *   - `curr` current completed index
 *   - `total` total number of ticks to complete
 *   - `width` the displayed width of the progress bar defaulting to total
 *   - `stream` the output stream defaulting to stderr
 *   - `head` head character defaulting to complete character
 *   - `complete` completion character defaulting to "="
 *   - `incomplete` incomplete character defaulting to "-"
 *   - `renderThrottle` minimum time between updates in milliseconds defaulting to 16
 *   - `callback` optional function to call when the progress bar completes
 *   - `clear` will clear the progress bar upon termination
 *
 * Tokens:
 *
 *   - `:bar` the progress bar itself
 *   - `:current` current tick number
 *   - `:total` total ticks
 *   - `:elapsed` time elapsed in seconds
 *   - `:percent` completion percentage
 *   - `:eta` eta in seconds
 *   - `:rate` rate of ticks per second
 *
 * @param {string} fmt
 * @param {object|number} options or total
 * @api public
 */

function ProgressBar(fmt, options) {
  this.stream = options.stream || process.stderr;

  if (typeof(options) == 'number') {
    var total = options;
    options = {};
    options.total = total;
  } else {
    options = options || {};
    if ('string' != typeof fmt) throw new Error('format required');
    if ('number' != typeof options.total) throw new Error('total required');
  }

  this.fmt = fmt;
  this.curr = options.curr || 0;
  this.total = options.total;
  this.width = options.width || this.total;
  this.clear = options.clear
  this.chars = {
    complete   : options.complete || '=',
    incomplete : options.incomplete || '-',
    head       : options.head || (options.complete || '=')
  };
  this.renderThrottle = options.renderThrottle !== 0 ? (options.renderThrottle || 16) : 0;
  this.callback = options.callback || function () {};
  this.tokens = {};
  this.lastDraw = '';
}

/**
 * "tick" the progress bar with optional `len` and optional `tokens`.
 *
 * @param {number|object} len or tokens
 * @param {object} tokens
 * @api public
 */

ProgressBar.prototype.tick = function(len, tokens){
  if (len !== 0)
    len = len || 1;

  // swap tokens
  if ('object' == typeof len) tokens = len, len = 1;
  if (tokens) this.tokens = tokens;

  // start time for eta
  if (0 == this.curr) this.start = new Date;

  this.curr += len

  // schedule render
  if (!this.renderThrottleTimeout) {
    this.renderThrottleTimeout = setTimeout(this.render.bind(this), this.renderThrottle);
  }

  // progress complete
  if (this.curr >= this.total) {
    if (this.renderThrottleTimeout) this.render();
    this.complete = true;
    this.terminate();
    this.callback(this);
    return;
  }
};

/**
 * Method to render the progress bar with optional `tokens` to place in the
 * progress bar's `fmt` field.
 *
 * @param {object} tokens
 * @api public
 */

ProgressBar.prototype.render = function (tokens) {
  clearTimeout(this.renderThrottleTimeout);
  this.renderThrottleTimeout = null;

  if (tokens) this.tokens = tokens;

  if (!this.stream.isTTY) return;

  var ratio = this.curr / this.total;
  ratio = Math.min(Math.max(ratio, 0), 1);

  var percent = ratio * 100;
  var incomplete, complete, completeLength;
  var elapsed = new Date - this.start;
  var eta = (percent == 100) ? 0 : elapsed * (this.total / this.curr - 1);
  var rate = this.curr / (elapsed / 1000);

  /* populate the bar template with percentages and timestamps */
  var str = this.fmt
    .replace(':current', this.curr)
    .replace(':total', this.total)
    .replace(':elapsed', isNaN(elapsed) ? '0.0' : (elapsed / 1000).toFixed(1))
    .replace(':eta', (isNaN(eta) || !isFinite(eta)) ? '0.0' : (eta / 1000)
      .toFixed(1))
    .replace(':percent', percent.toFixed(0) + '%')
    .replace(':rate', Math.round(rate));

  /* compute the available space (non-zero) for the bar */
  var availableSpace = Math.max(0, this.stream.columns - str.replace(':bar', '').length);
  if(availableSpace && process.platform === 'win32'){
    availableSpace = availableSpace - 1;
  }

  var width = Math.min(this.width, availableSpace);

  /* TODO: the following assumes the user has one ':bar' token */
  completeLength = Math.round(width * ratio);
  complete = Array(Math.max(0, completeLength + 1)).join(this.chars.complete);
  incomplete = Array(Math.max(0, width - completeLength + 1)).join(this.chars.incomplete);

  /* add head to the complete string */
  if(completeLength > 0)
    complete = complete.slice(0, -1) + this.chars.head;

  /* fill in the actual progress bar */
  str = str.replace(':bar', complete + incomplete);

  /* replace the extra tokens */
  if (this.tokens) for (var key in this.tokens) str = str.replace(':' + key, this.tokens[key]);

  if (this.lastDraw !== str) {
    this.stream.cursorTo(0);
    this.stream.write(str);
    this.stream.clearLine(1);
    this.lastDraw = str;
  }
};

/**
 * "update" the progress bar to represent an exact percentage.
 * The ratio (between 0 and 1) specified will be multiplied by `total` and
 * floored, representing the closest available "tick." For example, if a
 * progress bar has a length of 3 and `update(0.5)` is called, the progress
 * will be set to 1.
 *
 * A ratio of 0.5 will attempt to set the progress to halfway.
 *
 * @param {number} ratio The ratio (between 0 and 1 inclusive) to set the
 *   overall completion to.
 * @api public
 */

ProgressBar.prototype.update = function (ratio, tokens) {
  var goal = Math.floor(ratio * this.total);
  var delta = goal - this.curr;

  this.tick(delta, tokens);
};

/**
 * "interrupt" the progress bar and write a message above it.
 * @param {string} message The message to write.
 * @api public
 */

ProgressBar.prototype.interrupt = function (message) {
  // clear the current line
  this.stream.clearLine();
  // move the cursor to the start of the line
  this.stream.cursorTo(0);
  // write the message text
  this.stream.write(message);
  // terminate the line after writing the message
  this.stream.write('\n');
  // re-display the progress bar with its lastDraw
  this.stream.write(this.lastDraw);
};

/**
 * Terminates a progress bar.
 *
 * @api public
 */

ProgressBar.prototype.terminate = function () {
  if (this.clear) {
    if (this.stream.clearLine) {
      this.stream.clearLine();
      this.stream.cursorTo(0);
    }
  } else {
    this.stream.write('\n');
  }
};
<commit_msg>run render if throttle is zero<commit_after>/*!
 * node-progress
 * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Expose `ProgressBar`.
 */

exports = module.exports = ProgressBar;

/**
 * Initialize a `ProgressBar` with the given `fmt` string and `options` or
 * `total`.
 *
 * Options:
 *
 *   - `curr` current completed index
 *   - `total` total number of ticks to complete
 *   - `width` the displayed width of the progress bar defaulting to total
 *   - `stream` the output stream defaulting to stderr
 *   - `head` head character defaulting to complete character
 *   - `complete` completion character defaulting to "="
 *   - `incomplete` incomplete character defaulting to "-"
 *   - `renderThrottle` minimum time between updates in milliseconds defaulting to 16
 *   - `callback` optional function to call when the progress bar completes
 *   - `clear` will clear the progress bar upon termination
 *
 * Tokens:
 *
 *   - `:bar` the progress bar itself
 *   - `:current` current tick number
 *   - `:total` total ticks
 *   - `:elapsed` time elapsed in seconds
 *   - `:percent` completion percentage
 *   - `:eta` eta in seconds
 *   - `:rate` rate of ticks per second
 *
 * @param {string} fmt
 * @param {object|number} options or total
 * @api public
 */

function ProgressBar(fmt, options) {
  this.stream = options.stream || process.stderr;

  if (typeof(options) == 'number') {
    var total = options;
    options = {};
    options.total = total;
  } else {
    options = options || {};
    if ('string' != typeof fmt) throw new Error('format required');
    if ('number' != typeof options.total) throw new Error('total required');
  }

  this.fmt = fmt;
  this.curr = options.curr || 0;
  this.total = options.total;
  this.width = options.width || this.total;
  this.clear = options.clear
  this.chars = {
    complete   : options.complete || '=',
    incomplete : options.incomplete || '-',
    head       : options.head || (options.complete || '=')
  };
  this.renderThrottle = options.renderThrottle !== 0 ? (options.renderThrottle || 16) : 0;
  this.callback = options.callback || function () {};
  this.tokens = {};
  this.lastDraw = '';
}

/**
 * "tick" the progress bar with optional `len` and optional `tokens`.
 *
 * @param {number|object} len or tokens
 * @param {object} tokens
 * @api public
 */

ProgressBar.prototype.tick = function(len, tokens){
  if (len !== 0)
    len = len || 1;

  // swap tokens
  if ('object' == typeof len) tokens = len, len = 1;
  if (tokens) this.tokens = tokens;

  // start time for eta
  if (0 == this.curr) this.start = new Date;

  this.curr += len

  // schedule render
  if (this.renderThrottle === 0) {
    this.render();
  } else if (!this.renderThrottleTimeout) {
    this.renderThrottleTimeout = setTimeout(this.render.bind(this), this.renderThrottle);
  }

  // progress complete
  if (this.curr >= this.total) {
    if (this.renderThrottleTimeout) this.render();
    this.complete = true;
    this.terminate();
    this.callback(this);
    return;
  }
};

/**
 * Method to render the progress bar with optional `tokens` to place in the
 * progress bar's `fmt` field.
 *
 * @param {object} tokens
 * @api public
 */

ProgressBar.prototype.render = function (tokens) {
  clearTimeout(this.renderThrottleTimeout);
  this.renderThrottleTimeout = null;

  if (tokens) this.tokens = tokens;

  if (!this.stream.isTTY) return;

  var ratio = this.curr / this.total;
  ratio = Math.min(Math.max(ratio, 0), 1);

  var percent = ratio * 100;
  var incomplete, complete, completeLength;
  var elapsed = new Date - this.start;
  var eta = (percent == 100) ? 0 : elapsed * (this.total / this.curr - 1);
  var rate = this.curr / (elapsed / 1000);

  /* populate the bar template with percentages and timestamps */
  var str = this.fmt
    .replace(':current', this.curr)
    .replace(':total', this.total)
    .replace(':elapsed', isNaN(elapsed) ? '0.0' : (elapsed / 1000).toFixed(1))
    .replace(':eta', (isNaN(eta) || !isFinite(eta)) ? '0.0' : (eta / 1000)
      .toFixed(1))
    .replace(':percent', percent.toFixed(0) + '%')
    .replace(':rate', Math.round(rate));

  /* compute the available space (non-zero) for the bar */
  var availableSpace = Math.max(0, this.stream.columns - str.replace(':bar', '').length);
  if(availableSpace && process.platform === 'win32'){
    availableSpace = availableSpace - 1;
  }

  var width = Math.min(this.width, availableSpace);

  /* TODO: the following assumes the user has one ':bar' token */
  completeLength = Math.round(width * ratio);
  complete = Array(Math.max(0, completeLength + 1)).join(this.chars.complete);
  incomplete = Array(Math.max(0, width - completeLength + 1)).join(this.chars.incomplete);

  /* add head to the complete string */
  if(completeLength > 0)
    complete = complete.slice(0, -1) + this.chars.head;

  /* fill in the actual progress bar */
  str = str.replace(':bar', complete + incomplete);

  /* replace the extra tokens */
  if (this.tokens) for (var key in this.tokens) str = str.replace(':' + key, this.tokens[key]);

  if (this.lastDraw !== str) {
    this.stream.cursorTo(0);
    this.stream.write(str);
    this.stream.clearLine(1);
    this.lastDraw = str;
  }
};

/**
 * "update" the progress bar to represent an exact percentage.
 * The ratio (between 0 and 1) specified will be multiplied by `total` and
 * floored, representing the closest available "tick." For example, if a
 * progress bar has a length of 3 and `update(0.5)` is called, the progress
 * will be set to 1.
 *
 * A ratio of 0.5 will attempt to set the progress to halfway.
 *
 * @param {number} ratio The ratio (between 0 and 1 inclusive) to set the
 *   overall completion to.
 * @api public
 */

ProgressBar.prototype.update = function (ratio, tokens) {
  var goal = Math.floor(ratio * this.total);
  var delta = goal - this.curr;

  this.tick(delta, tokens);
};

/**
 * "interrupt" the progress bar and write a message above it.
 * @param {string} message The message to write.
 * @api public
 */

ProgressBar.prototype.interrupt = function (message) {
  // clear the current line
  this.stream.clearLine();
  // move the cursor to the start of the line
  this.stream.cursorTo(0);
  // write the message text
  this.stream.write(message);
  // terminate the line after writing the message
  this.stream.write('\n');
  // re-display the progress bar with its lastDraw
  this.stream.write(this.lastDraw);
};

/**
 * Terminates a progress bar.
 *
 * @api public
 */

ProgressBar.prototype.terminate = function () {
  if (this.clear) {
    if (this.stream.clearLine) {
      this.stream.clearLine();
      this.stream.cursorTo(0);
    }
  } else {
    this.stream.write('\n');
  }
};
<|endoftext|>