<commit_before>var util = require("util"),
    url = require("url"),
    path = require("path"),
    repl = require("repl"),
    colors = require("colors"),
    chrome = require("chrome-remote-interface");

const PROP_SHOW_COUNT = 5;

module.exports = ChromeREPL;

function ChromeREPL() {}

ChromeREPL.prototype = {

  start: function(options) {
    this.connect(options, function(err, tab) {
      if (err) throw err;
      var self = this;

      console.log(tab.url.yellow);

      this.setTab(tab, function() {
        if (!self.repl) {
          self.repl = repl.start({
            prompt: self.getPrompt(),
            eval: self.eval.bind(self),
            input: process.stdin,
            output: process.stdout,
            writer: self.writer.bind(self)
          });
          self.defineCommands();
          self.replComplete  = self.repl.complete;
          self.repl.complete = self.complete.bind(self);
        } else {
          self.repl.displayPrompt();
        }
      });

    }.bind(this))
  },

  complete: function (line, callback) {
    var self = this;
    // TODO: .tab .switch .exit .help completions (but don't do local context completions)
    //this.replComplete.call(this.repl, line, function(err, cs) {
    //    return callback(null, cs);
    //  if (cs[0].length != 0) // we have repl command completions, don't need to avaluate in browser
    //     return callback(null, cs);
      self.chromeCompleter(line, callback);
    //});
  },

  injectedCompleterObj: function getCompletions(primitiveType) {
    var object;
    if (primitiveType==="string")
      object = new String("");
    else if(primitiveType==="number")
      object = new Number(0);
    else if(primitiveType==="boolean")
      object = new Boolean(false);
    else
      object=this;
    var resultSet={};
    for(var o=object;o;o=o.__proto__)
    {
      try {
        var names=Object.getOwnPropertyNames(o);
        for(var i=0;i<names.length;++i)
          resultSet[names[i]]=true;
      } catch(e) {}
    }
    return resultSet;
  },

  chromeCompleter: function (line, callback) {
    // TODO: object properties completions only atm
    // TODO: to add numbers, strings, etc use this evals:
    // evaluate( '(' + self.injectedCompleterObj.toString() + ')(\"string\");
    // evaluate( '(' + self.injectedCompleterObj.toString() + ')(\"number\");
    // evaluate( '(' + self.injectedCompleterObj.toString() + ')(\"boolean"\);

    var self = this;

    var completionsForObj = function(id, callback) {
      var params = {
        objectId: id,
        functionDeclaration: self.injectedCompleterObj.toString(),
        doNotPauseOnExceptionsAndMuteConsole: true,
        returnByValue: true,
        generatePreview:false
      };
      self.client.Runtime.callFunctionOn(params, function(err, res) {
         callback(err, res);
      });
    };
    var lastExpr = line.trim().split(/[ {}();\/\\]+/).slice(-1)[0];
    if (!lastExpr)
      return callback(null, [[], line]);
    // TODO obj['longproperyname completer in addition to obj.longproperyname
    var path = lastExpr.split('.');
    var expr, partial;
    if (path.length === 0) {
       expr = 'this';
       partial = '';
    } else if (path.length === 1) {
       expr = 'this';
       partial = path[0];
    } else {
       expr = path.slice(0, -1).join('.');
       partial = path[path.length -1];
    }
    var lineStart = line.slice(0, lastExpr.length - partial.length);

    // repl comands, not chrome completion
    var lineDot = line.split('.');
    if (lineDot.length > 1 && lineDot[0].trim() === '') {
      partial = lineDot[1];
      completions = Object.keys(self.repl.commands)
        .filter(function(c) { return c.indexOf('.' + partial) == 0 })
        .map(   function(c) { return lineDot[0] + c });
      return callback(null, [completions, line]);
    }

    var evalParams = {
       expression: expr,
       objectGroup: "completion",
       includeCommandLineAPI: true,
       doNotPauseOnExceptionsAndMuteConsole: true,
       contextId: self.runtimeContext.id,
       returnByValue:false,
       generatePreview:false
    }
    this.client.Runtime.evaluate(evalParams, function(err, res) {

      function done(params) {
        self.client.Runtime.releaseObjectGroup({"objectGroup":"completion"});
        callback(null, params);
      }
      if (res.wasThrown)
        return done(null, [[], line]);
      if (res.result.type === 'object') {
        completionsForObj(res.result.objectId, function(err, completions) {
           var allProps = Object.keys(completions.result.value);
           var completions = allProps
             .filter(function(c) { return c.indexOf(partial) == 0 })
             .map(   function(c) { return lineStart + c });
           done([completions, line]);
        });
      } else {
        // TODO get completions for String and Number
        done([[], line]);
      }
    });
  },

  connect: function(options, cb) {
    var self = this;
    self.options = options;
    var client = chrome.createClient();
    client.on("error", function(error) {
      console.log(error);
    });
    self.client = client;
    client.listTabs(options.host, options.port, function(err, tabs) {
      if(err) return cb(err);
      cb(null, tabs[0]);
    });
  },

  writer: function(output) {

    if (!output)
      return ''

    function propertyValue(t, v) {
      if (t === 'string')
        return util.inspect(v, { colors: true });
      else if (t === 'number' || t === 'boolean')
        return v.toString().blue;
      else if (t === 'object' && v == 'null')
        return 'null'.blue;
      else if (t === 'undefined')
        return 'undefined'.gray;
      return t.grey;
    }
    function propertyPreview(p) {
      return p.name.magenta + ': ' + propertyValue(p.type, p.value);
    }
    function showPreview(obj) {
      return obj.className.yellow + " { " +
         obj.preview.properties.map(propertyPreview).join(', ') +
      " }";
    }
    if (output.wasThrown) {
      return output.result.description.red;
    }
    if (output.result.type == 'undefined')
      return ''; //return 'undefined'.gray;

    if (!output || output.result.type != "object") {
      // let inspect do its thing if it's a literal
      return util.inspect(output.result.value, { colors: true });
    }
    if (output.result.type === "object") {
      return showPreview(output.result);
    }
  },

  write: function(str, cb) {
    this.repl.outputStream.write(str, cb);
  },

  writeLn: function(str, cb) {
    this.repl.prompt = '';
    this.repl.displayPrompt();
    this.repl.outputStream.write(str + '\n');
    this.repl.prompt = this.getPrompt();
    //this.repl.displayPrompt();
    if (cb) cb();
  },

  setTab: function(tab, cb) {
    this.tab = tab;
    var self = this;
    this.client.connectToWebSocket(tab.webSocketDebuggerUrl);
    this.client.removeAllListeners();
    this.client.on('connect', function() {
      cb();
      self.client.Runtime.disable();
      self.client.Runtime.enable();
      self.client.Runtime.executionContextCreated(function(ctxInfo) {
        self.runtimeContext = ctxInfo.context
      });
      self.client.Console.enable();
      function handleMessage(message) {
        // TODO: handle objects. reuse eval's mirroring
        //console.log(JSON.stringify(message, null, 4));
        var stack = message.message.stackTrace;
        if (message.message.level !== 'log')
          return;
        var prefix = "> ".blue;
        var messageText = prefix + message.message.text;
        //if (stack.length > 1 && stack[1].functionName == 'InjectedScript._evaluateOn')
        //  self.write('\n' + messageText + '\n');   // assume it's invoked from console - we don't need to scroll screen
        //else
          self.writeLn(messageText); // assume it's from user interaction - insert message above prompt
        self.repl.displayPrompt();
      }
      self.client.Console.messageAdded(function(message) {
        self.lastMessage = message;
        handleMessage(message);
      });
      // TODO: implement counter. Meanwhile, just repeat last message
      self.client.Console.messageRepeatCountUpdated(function() {
        handleMessage(self.lastMessage);
      });
      self.repl.prompt = self.getPrompt();
      self.repl.displayPrompt();
    });
  },

  getPrompt: function() {
    var parts = url.parse(this.tab.url);

    var name = parts.hostname;
    if (!name) {
      name = path.basename(parts.path);
    }
    return name + "> ";
  },

  // compliant with node REPL module eval function reqs
  eval: function(cmd, context, filename, cb) {
    this.evalInTab(cmd, cb);
  },

  evalInTab: function(input, cb) {
    this.client.Runtime.evaluate({expression: input.slice(1, -2), generatePreview: true}, function(err, resp) {
      return cb(null, resp);
    });
  },

  transformResult: function(result) {
    switch (result.type) {
      case "undefined":
        return undefined;
      case "null":
        return null;
    }
    return result;
  },

  defineCommands: function() {
    this.repl.defineCommand('tabs', {
      help: 'list currently open tabs',
      action: this.listTabs.bind(this)
    })

    this.repl.defineCommand('quit', {
      help: 'quit crconsole',
      action: this.quit
    })

    this.repl.defineCommand('switch', {
      help: 'switch to evaluating in another tab by index',
      action: this.switchTab.bind(this)
    })
  },

  switchTab: function(index) {
    this.client.close();
    var self = this;
    this.client.listTabs(this.options.host, this.options.port, function(err, tabs) {
      if (err) throw err;
      var tab = tabs[index];

      if (!tab) {
        this.write("no tab at index " + index + "\n");
        this.repl.displayPrompt();
      }
      else {
        self.setTab(tab, function() {
          self.write((self.tab.url + "\n").yellow);
        });
      }

    }.bind(this));
  },

  listTabs: function() {
    this.client.listTabs(this.host, this.options.port, function(err, tabs) {
      if (err) throw err;

      var strs = "";
      for (var i in tabs) {
        strs += "[" + i + "] " + tabs[i].url + "\n";
      }

      this.write(strs);
      this.repl.displayPrompt();
    }.bind(this));
  },

  quit: function() {
    process.exit(0);
  }
}
<commit_msg>fix iojs and node 0.12 related prompt. closes #10<commit_after>var util = require("util"),
    url = require("url"),
    path = require("path"),
    repl = require("repl"),
    colors = require("colors"),
    chrome = require("chrome-remote-interface");

const PROP_SHOW_COUNT = 5;

module.exports = ChromeREPL;

function ChromeREPL() {}

ChromeREPL.prototype = {

  start: function(options) {
    this.connect(options, function(err, tab) {
      if (err) throw err;
      var self = this;

      console.log(tab.url.yellow);

      this.setTab(tab, function() {
        if (!self.repl) {
          self.repl = repl.start({
            prompt: self.getPrompt(),
            eval: self.eval.bind(self),
            input: process.stdin,
            output: process.stdout,
            writer: self.writer.bind(self)
          });
          if (!self.repl.setPrompt) { // assume node pre0.12 or io.js
            self.repl.setPrompt = function(p) {
              self.repl.prompt = p;
            };
          }
          self.defineCommands();
          self.replComplete  = self.repl.complete;
          self.repl.complete = self.complete.bind(self);
        } else {
          self.repl.displayPrompt();
        }
      });

    }.bind(this))
  },

  complete: function (line, callback) {
    var self = this;
    // TODO: .tab .switch .exit .help completions (but don't do local context completions)
    //this.replComplete.call(this.repl, line, function(err, cs) {
    //    return callback(null, cs);
    //  if (cs[0].length != 0) // we have repl command completions, don't need to avaluate in browser
    //     return callback(null, cs);
      self.chromeCompleter(line, callback);
    //});
  },

  injectedCompleterObj: function getCompletions(primitiveType) {
    var object;
    if (primitiveType==="string")
      object = new String("");
    else if(primitiveType==="number")
      object = new Number(0);
    else if(primitiveType==="boolean")
      object = new Boolean(false);
    else
      object=this;
    var resultSet={};
    for(var o=object;o;o=o.__proto__)
    {
      try {
        var names=Object.getOwnPropertyNames(o);
        for(var i=0;i<names.length;++i)
          resultSet[names[i]]=true;
      } catch(e) {}
    }
    return resultSet;
  },

  chromeCompleter: function (line, callback) {
    // TODO: object properties completions only atm
    // TODO: to add numbers, strings, etc use this evals:
    // evaluate( '(' + self.injectedCompleterObj.toString() + ')(\"string\");
    // evaluate( '(' + self.injectedCompleterObj.toString() + ')(\"number\");
    // evaluate( '(' + self.injectedCompleterObj.toString() + ')(\"boolean"\);

    var self = this;

    var completionsForObj = function(id, callback) {
      var params = {
        objectId: id,
        functionDeclaration: self.injectedCompleterObj.toString(),
        doNotPauseOnExceptionsAndMuteConsole: true,
        returnByValue: true,
        generatePreview:false
      };
      self.client.Runtime.callFunctionOn(params, function(err, res) {
         callback(err, res);
      });
    };
    var lastExpr = line.trim().split(/[ {}();\/\\]+/).slice(-1)[0];
    if (!lastExpr)
      return callback(null, [[], line]);
    // TODO obj['longproperyname completer in addition to obj.longproperyname
    var path = lastExpr.split('.');
    var expr, partial;
    if (path.length === 0) {
       expr = 'this';
       partial = '';
    } else if (path.length === 1) {
       expr = 'this';
       partial = path[0];
    } else {
       expr = path.slice(0, -1).join('.');
       partial = path[path.length -1];
    }
    var lineStart = line.slice(0, lastExpr.length - partial.length);

    // repl comands, not chrome completion
    var lineDot = line.split('.');
    if (lineDot.length > 1 && lineDot[0].trim() === '') {
      partial = lineDot[1];
      completions = Object.keys(self.repl.commands)
        .filter(function(c) { return c.indexOf('.' + partial) == 0 })
        .map(   function(c) { return lineDot[0] + c });
      return callback(null, [completions, line]);
    }

    var evalParams = {
       expression: expr,
       objectGroup: "completion",
       includeCommandLineAPI: true,
       doNotPauseOnExceptionsAndMuteConsole: true,
       contextId: self.runtimeContext.id,
       returnByValue:false,
       generatePreview:false
    }
    this.client.Runtime.evaluate(evalParams, function(err, res) {

      function done(params) {
        self.client.Runtime.releaseObjectGroup({"objectGroup":"completion"});
        callback(null, params);
      }
      if (res.wasThrown)
        return done(null, [[], line]);
      if (res.result.type === 'object') {
        completionsForObj(res.result.objectId, function(err, completions) {
           var allProps = Object.keys(completions.result.value);
           var completions = allProps
             .filter(function(c) { return c.indexOf(partial) == 0 })
             .map(   function(c) { return lineStart + c });
           done([completions, line]);
        });
      } else {
        // TODO get completions for String and Number
        done([[], line]);
      }
    });
  },

  connect: function(options, cb) {
    var self = this;
    self.options = options;
    var client = chrome.createClient();
    client.on("error", function(error) {
      console.log(error);
    });
    self.client = client;
    client.listTabs(options.host, options.port, function(err, tabs) {
      if(err) return cb(err);
      cb(null, tabs[0]);
    });
  },

  writer: function(output) {
    if (!output)
      return ''

    function propertyValue(t, v) {
      if (t === 'string')
        return util.inspect(v, { colors: true });
      else if (t === 'number' || t === 'boolean')
        return v.toString().blue;
      else if (t === 'object' && v == 'null')
        return 'null'.blue;
      else if (t === 'undefined')
        return 'undefined'.gray;
      return t.grey;
    }
    function propertyPreview(p) {
      return p.name.magenta + ': ' + propertyValue(p.type, p.value);
    }
    function showPreview(obj) {
      if (!obj.preview) {
        return obj.className.yellow;
      }
      return obj.className.yellow + " { " +
         obj.preview.properties.map(propertyPreview).join(', ') +
      " }";
    }
    if (output.wasThrown) {
      return output.result.description.red;
    }
    if (output.result.type == 'function') {
      return '[Function]'.cyan;
    }
    if (output.result.type == 'undefined')
      return 'undefined'.gray;

    if (!output || output.result.type != "object") {
      // let inspect do its thing if it's a literal
      return util.inspect(output.result.value, { colors: true });
    }
    if (output.result.type === "object") {
      return showPreview(output.result);
    }
  },

  write: function(str, cb) {
    this.repl.outputStream.write(str, cb);
  },

  writeLn: function(str, cb) {
    this.repl.setPrompt('');
    this.repl.displayPrompt();
    this.repl.outputStream.write(str + '\n');
    this.repl.setPrompt(this.getPrompt());
    //this.repl.displayPrompt();
    if (cb) cb();
  },

  setTab: function(tab, cb) {
    this.tab = tab;
    var self = this;
    this.client.connectToWebSocket(tab.webSocketDebuggerUrl);
    this.client.removeAllListeners();
    this.client.on('connect', function() {
      cb();
      self.client.Runtime.disable();
      self.client.Runtime.enable();
      self.client.Runtime.executionContextCreated(function(ctxInfo) {
        self.runtimeContext = ctxInfo.context
      });
      self.client.Console.enable();
      function handleMessage(message) {
        // TODO: handle objects. reuse eval's mirroring
        //console.log(JSON.stringify(message, null, 4));
        var stack = message.message.stackTrace;
        if (message.message.level !== 'log')
          return;
        var prefix = "> ".blue;
        var messageText = prefix + message.message.text;
        //if (stack.length > 1 && stack[1].functionName == 'InjectedScript._evaluateOn')
        //  self.write('\n' + messageText + '\n');   // assume it's invoked from console - we don't need to scroll screen
        //else
          self.writeLn(messageText); // assume it's from user interaction - insert message above prompt
        self.repl.displayPrompt();
      }
      self.client.Console.messageAdded(function(message) {
        self.lastMessage = message;
        handleMessage(message);
      });
      // TODO: implement counter. Meanwhile, just repeat last message
      self.client.Console.messageRepeatCountUpdated(function() {
        handleMessage(self.lastMessage);
      });
      self.repl.setPrompt(self.getPrompt());
    });
  },

  getPrompt: function() {
    var parts = url.parse(this.tab.url);

    var name = parts.hostname;
    if (!name) {
      name = path.basename(parts.path);
    }
    return name + "> ";
  },

  // compliant with node REPL module eval function reqs
  eval: function(cmd, context, filename, cb) {
    this.evalInTab(cmd, cb);
  },

  evalInTab: function(input, cb) {
    var removeBrackets = function(input) {
      // node repl adds () to eval input while iojs not
      // try to detect here and remove
      if (input.slice(-2) === '\n)' && input.slice(0,1) == '(')
        return input.slice(1,-2);
      return input;
    };
    this.client.Runtime.evaluate({expression: removeBrackets(input), generatePreview: true}, function(err, resp) {
      return cb(null, resp);
    });
  },

  transformResult: function(result) {
    switch (result.type) {
      case "undefined":
        return undefined;
      case "null":
        return null;
    }
    return result;
  },

  defineCommands: function() {
    this.repl.defineCommand('tabs', {
      help: 'list currently open tabs',
      action: this.listTabs.bind(this)
    })

    this.repl.defineCommand('quit', {
      help: 'quit crconsole',
      action: this.quit
    })

    this.repl.defineCommand('switch', {
      help: 'switch to evaluating in another tab by index',
      action: this.switchTab.bind(this)
    })
  },

  switchTab: function(index) {
    this.client.close();
    var self = this;
    this.client.listTabs(this.options.host, this.options.port, function(err, tabs) {
      if (err) throw err;
      var tab = tabs[index];

      if (!tab) {
        this.write("no tab at index " + index + "\n");
      }
      else {
        self.setTab(tab, function() {
          self.write((self.tab.url + "\n").yellow);
        });
      }

    }.bind(this));
  },

  listTabs: function() {
    this.client.listTabs(this.host, this.options.port, function(err, tabs) {
      if (err) throw err;

      var strs = "";
      for (var i in tabs) {
        strs += "[" + i + "] " + tabs[i].url + "\n";
      }

      this.write(strs);
      this.repl.displayPrompt();
    }.bind(this));
  },

  quit: function() {
    process.exit(0);
  }
}
<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var Emitter = require('component-emitter');

var utils = require('./lib/utils');
var Task = require('./lib/task');
var noop = require('./lib/noop');
var map = require('./lib/map-deps');
var session = require('./lib/session');
var flowFactory = require('./lib/flow');

/**
 * Composer constructor. Create a new Composer
 *
 * ```js
 * var composer = new Composer();
 * ```
 *
 * @api public
 */

function Composer(name) {
  Emitter.call(this);
  this.name = name || 'composer';
  this.tasks = {};
}

/**
 * Mix in `Emitter` methods
 */

Emitter(Composer.prototype);

/**
 * Register a new task with it's options and dependencies.
 *
 * Options:
 *
 *  - `deps`: array of dependencies
 *  - `flow`: How this task will be executed with it's dependencies (`series`, `parallel`, `settleSeries`, `settleParallel`)
 *
 * ```js
 * composer.task('site', ['styles'], function () {
 *   return app.src('templates/pages/*.hbs')
 *     .pipe(app.dest('_gh_pages'));
 * });
 * ```
 *
 * @param  {String} `name` Name of the task to register
 * @param {Object} `options` Options to set dependencies or control flow.
 * @param {String|Array|Function} `deps` Additional dependencies for this task.
 * @param {Function} `fn` Final function is the task to register.
 * @return {Object} Return `this` for chaining
 * @api public
 */

Composer.prototype.task = function(name/*, options, dependencies and task */) {
  // return the currently running task
  // when no name is given
  if (arguments.length === 0) {
    return session(this.name).get('task');
  }

  var deps = [].concat.apply([], [].slice.call(arguments, 1));
  var options = {};
  var fn = noop;
  if (typeof deps[deps.length-1] === 'function') {
    fn = deps.pop();
  }

  if (deps.length && utils.isobject(deps[0])) {
    options = deps.shift();
  }

  options.deps = deps
    .concat(options.deps || [])
    .map(map.bind(this));

  var task = new Task({
    name: name,
    options: options,
    fn: fn,
    session: session(this.name),
    app: this
  });

  // bubble up events from tasks
  task.on('starting', this.emit.bind(this, 'starting'));
  task.on('finished', this.emit.bind(this, 'finished'));
  task.on('error', this.emit.bind(this, 'error'));

  this.tasks[name] = task;
  return this;
};

/**
 * Build a task or list of tasks.
 *
 * ```js
 * composer.build('default', function (err, results) {
 *   if (err) return console.error(err);
 *   console.log(results);
 * });
 * ```
 *
 * @param {String|Array|Function} `tasks` List of tasks by name, function, or array of names/functions.
 * @param {Function} `cb` Callback function to be called when all tasks are finished building.
 * @api public
 */

Composer.prototype.build = function(/* list of tasks/functions to build */) {
  var args = [].concat.apply([], [].slice.call(arguments));
  var done = args.pop();
  if (typeof done !== 'function') {
    throw new TypeError('Expected the last argument to be a callback function, but got `' + typeof done + '`.');
  }
  var fn = this.series.apply(this, args);
  return fn(done);
};

/**
 * Compose task or list of tasks into a single function that runs the tasks in series.
 *
 * ```js
 * composer.task('foo', function (done) {
 *   console.log('this is foo');
 *   done();
 * });
 *
 * var fn = composer.series('foo', function bar(done) {
 *   console.log('this is bar');
 *   done();
 * });
 *
 * fn(function (err) {
 *   if (err) return console.error(err);
 *   console.log('done');
 * });
 * //=> this is foo
 * //=> this is bar
 * //=> done
 * ```
 *
 * @param {String|Array|Function} `tasks` List of tasks by name, function, or array of names/functions.
 * @return {Function} Composed function that may take a callback function.
 * @api public
 */

Composer.prototype.series = flowFactory('series');

/**
 * Compose task or list of tasks into a single function that runs the tasks in parallel.
 *
 * ```js
 * composer.task('foo', function (done) {
 *   setTimeout(function () {
 *     console.log('this is foo');
 *     done();
 *   }, 500);
 * });
 *
 * var fn = composer.parallel('foo', function bar(done) {
 *   console.log('this is bar');
 *   done();
 * });
 *
 * fn(function (err) {
 *   if (err) return console.error(err);
 *   console.log('done');
 * });
 * //=> this is bar
 * //=> this is foo
 * //=> done
 * ```
 *
 * @param {String|Array|Function} `tasks` List of tasks by name, function, or array of names/functions.
 * @return {Function} Composed function that may take a callback function.
 * @api public
 */

Composer.prototype.parallel = flowFactory('parallel');

/**
 * Watch a file, directory, or glob pattern for changes and build a task or list of tasks
 * when changes are made. Watch is powered by [chokidar][] so the glob pattern may be
 * anything that [chokidar.watch](https://github.com/paulmillr/chokidar#api) accepts.
 *
 * ```js
 * var watcher = composer.watch('templates/pages/*.hbs', ['site']);
 * ```
 *
 * @param  {String|Array} `glob` Filename, Directory name, or glob pattern to watch
 * @param  {Object} `options` Additional options to be passed to [chokidar][]
 * @param  {String|Array|Function} `tasks` Tasks that are passed to `.build` when files in the glob are changed.
 * @return {Object} Returns an instance of `FSWatcher` from [chokidar][]
 * @api public
 */

Composer.prototype.watch = function(glob, options/*, fns/tasks */) {
  var self = this;
  var len = arguments.length - 1, i = 0;
  var args = new Array(len + 1);
  while (len--) args[i] = arguments[++i];
  args[i] = done;

  var opts = {};
  if (typeof options === 'object' && !Array.isArray(options)) {
    args.shift();
    opts = utils.extend(opts, options);
  }

  var building = true;
  function done (err) {
    building = false;
    if (err) console.error(err);
  }

  var watch = utils.chokidar.watch(glob, opts);

  // only contains our `done` function
  if (args.length === 1) {
    return watch;
  }

  watch
    .on('ready', function () {
      building = false;
    })
    .on('all', function () {
      if (building) return;
      building = true;
      self.build.apply(self, args);
    });

  return watch;
};

/**
 * Expose Composer
 */

module.exports = Composer;
<commit_msg>using hidden property `_appname` instead of `name` throwing an error when calling `.task` without a name adding `.currentTask` property that returns the currently running task<commit_after>'use strict';

var util = require('util');
var Emitter = require('component-emitter');

var utils = require('./lib/utils');
var Task = require('./lib/task');
var noop = require('./lib/noop');
var map = require('./lib/map-deps');
var session = require('./lib/session');
var flowFactory = require('./lib/flow');

/**
 * Composer constructor. Create a new Composer
 *
 * ```js
 * var composer = new Composer();
 * ```
 *
 * @api public
 */

function Composer(name) {
  Emitter.call(this);
  utils.define(this, '_appname', name || 'composer');
  utils.define(this, 'currentTask', {
    get: function() {
      return session(this._appname).get('task');
    }
  });
  this.tasks = {};
}

/**
 * Mix in `Emitter` methods
 */

Emitter(Composer.prototype);

/**
 * Register a new task with it's options and dependencies.
 *
 * Options:
 *
 *  - `deps`: array of dependencies
 *  - `flow`: How this task will be executed with it's dependencies (`series`, `parallel`, `settleSeries`, `settleParallel`)
 *
 * ```js
 * composer.task('site', ['styles'], function () {
 *   return app.src('templates/pages/*.hbs')
 *     .pipe(app.dest('_gh_pages'));
 * });
 * ```
 *
 * @param  {String} `name` Name of the task to register
 * @param {Object} `options` Options to set dependencies or control flow.
 * @param {String|Array|Function} `deps` Additional dependencies for this task.
 * @param {Function} `fn` Final function is the task to register.
 * @return {Object} Return `this` for chaining
 * @api public
 */

Composer.prototype.task = function(name/*, options, dependencies and task */) {
  if (typeof name !== 'string') {
    throw new TypeError('expected `name` to be a string');
  }

  var deps = [].concat.apply([], [].slice.call(arguments, 1));
  var options = {};
  var fn = noop;
  if (typeof deps[deps.length-1] === 'function') {
    fn = deps.pop();
  }

  if (deps.length && utils.isobject(deps[0])) {
    options = deps.shift();
  }

  options.deps = deps
    .concat(options.deps || [])
    .map(map.bind(this));

  var task = new Task({
    name: name,
    options: options,
    fn: fn,
    session: session(this._appname),
    app: this
  });

  // bubble up events from tasks
  task.on('starting', this.emit.bind(this, 'starting'));
  task.on('finished', this.emit.bind(this, 'finished'));
  task.on('error', this.emit.bind(this, 'error'));

  this.tasks[name] = task;
  return this;
};

/**
 * Build a task or list of tasks.
 *
 * ```js
 * composer.build('default', function (err, results) {
 *   if (err) return console.error(err);
 *   console.log(results);
 * });
 * ```
 *
 * @param {String|Array|Function} `tasks` List of tasks by name, function, or array of names/functions.
 * @param {Function} `cb` Callback function to be called when all tasks are finished building.
 * @api public
 */

Composer.prototype.build = function(/* list of tasks/functions to build */) {
  var args = [].concat.apply([], [].slice.call(arguments));
  var done = args.pop();
  if (typeof done !== 'function') {
    throw new TypeError('Expected the last argument to be a callback function, but got `' + typeof done + '`.');
  }
  var fn = this.series.apply(this, args);
  return fn(done);
};

/**
 * Compose task or list of tasks into a single function that runs the tasks in series.
 *
 * ```js
 * composer.task('foo', function (done) {
 *   console.log('this is foo');
 *   done();
 * });
 *
 * var fn = composer.series('foo', function bar(done) {
 *   console.log('this is bar');
 *   done();
 * });
 *
 * fn(function (err) {
 *   if (err) return console.error(err);
 *   console.log('done');
 * });
 * //=> this is foo
 * //=> this is bar
 * //=> done
 * ```
 *
 * @param {String|Array|Function} `tasks` List of tasks by name, function, or array of names/functions.
 * @return {Function} Composed function that may take a callback function.
 * @api public
 */

Composer.prototype.series = flowFactory('series');

/**
 * Compose task or list of tasks into a single function that runs the tasks in parallel.
 *
 * ```js
 * composer.task('foo', function (done) {
 *   setTimeout(function () {
 *     console.log('this is foo');
 *     done();
 *   }, 500);
 * });
 *
 * var fn = composer.parallel('foo', function bar(done) {
 *   console.log('this is bar');
 *   done();
 * });
 *
 * fn(function (err) {
 *   if (err) return console.error(err);
 *   console.log('done');
 * });
 * //=> this is bar
 * //=> this is foo
 * //=> done
 * ```
 *
 * @param {String|Array|Function} `tasks` List of tasks by name, function, or array of names/functions.
 * @return {Function} Composed function that may take a callback function.
 * @api public
 */

Composer.prototype.parallel = flowFactory('parallel');

/**
 * Watch a file, directory, or glob pattern for changes and build a task or list of tasks
 * when changes are made. Watch is powered by [chokidar][] so the glob pattern may be
 * anything that [chokidar.watch](https://github.com/paulmillr/chokidar#api) accepts.
 *
 * ```js
 * var watcher = composer.watch('templates/pages/*.hbs', ['site']);
 * ```
 *
 * @param  {String|Array} `glob` Filename, Directory name, or glob pattern to watch
 * @param  {Object} `options` Additional options to be passed to [chokidar][]
 * @param  {String|Array|Function} `tasks` Tasks that are passed to `.build` when files in the glob are changed.
 * @return {Object} Returns an instance of `FSWatcher` from [chokidar][]
 * @api public
 */

Composer.prototype.watch = function(glob, options/*, fns/tasks */) {
  var self = this;
  var len = arguments.length - 1, i = 0;
  var args = new Array(len + 1);
  while (len--) args[i] = arguments[++i];
  args[i] = done;

  var opts = {};
  if (typeof options === 'object' && !Array.isArray(options)) {
    args.shift();
    opts = utils.extend(opts, options);
  }

  var building = true;
  function done (err) {
    building = false;
    if (err) console.error(err);
  }

  var watch = utils.chokidar.watch(glob, opts);

  // only contains our `done` function
  if (args.length === 1) {
    return watch;
  }

  watch
    .on('ready', function () {
      building = false;
    })
    .on('all', function () {
      if (building) return;
      building = true;
      self.build.apply(self, args);
    });

  return watch;
};

/**
 * Expose Composer
 */

module.exports = Composer;
<|endoftext|>


<commit_before>'use strict';

var EventEmitter = require('events').EventEmitter
  , Primus = require('primus')
  , fuse = require('fusing')
  , path = require('path');

/**
 * Create a new Primacron server.
 *
 * @constructor
 * @param {Server} server HTTP server.
 * @param {Object} options Optional configuration.
 * @api private
 */
function Primacron(server, options) {
  if (!(this instanceof Primacron)) return new Primacron(server, options);

  options = this.configurable(options || server);

  //
  // In Node, all servers inherit from the EventEmitter or net.Server. This is
  // gives us a some what reliable check to see if we need to create a server.
  // By allowing EventEmitters we can also compile client code without creating
  // an actual HTTP server.
  //
  if (!(server instanceof EventEmitter)) {
    server = require('create-server')(server || options);
  }

  var listening = !!server.listeners('listening').length;

  this.fuse([server, options]);

  this.use('fortress maximus', require('fortress-maximus'));
  this.use('omega supreme', require('omega-supreme'));
  this.use('emit', require('primus-emit/broadcast'));
  this.use('metroplex', require('metroplex'));
  this.use('mirage', require('mirage'));

  //
  // If the provided options tell the create-server to automatically start
  // listening on the server we need to automatically call the .listen method so
  // we can assign the correct listeners.
  //
  if (listening) {
    this.listen();
  }
}

fuse(Primacron, Primus);

/**
 * Return a pre-configured configuration for Primus.
 *
 * @param {Object} options Given optional options.
 * @returns {Object} Pre configured objects.
 * @api private
 */
Primacron.readable('configurable', function configurable(options) {
  if ('object' !== typeof options) options = {};

  var pathname = options.pathname || '/primacron';

  return this.merge({
    url: path.resolve(pathname, './omega/supreme'),
    transformer: 'engine.io',
    namespace: 'primacron',
    pathname: pathname,
    fortress: 'primus',
    parser: 'JSON'
  }, options);
});

/**
 * This argument accepts what ever you want to send to a regular server.listen
 * method.
 *
 * @api public
 */
Primacron.readable('listen', function listen() {
  var listening = !!this.server.listeners('listening').length;

  //
  // Proxy the events of the HTTP server to our own Primacron instance.
  //
  this.server.on('listening', this.emits('listening'));
  this.server.on('error', this.emits('error'));
  this.server.on('close', this.emits('close'));

  //
  // Proxy all arguments to the server if we're not already listening
  //
  if (!listening && this.server.listen) {
    this.server.listen.apply(this.server, arguments);
  }
});

//
// Add missing methods of a regular HTTP server that we can proxy from our
// internal `this.server` instance.
//
['address', 'getConnections'].forEach(function missing(method) {
  Primacron.readable(method, function proxy() {
    var res = this.server[method].apply(this.server, arguments);

    //
    // Figure out which kind of value we should return. If this was a chaining
    // method on the server we should just return Primacron instead. If it's not
    // the server that is returned we should return the value.
    //
    if (res === this.server) return this;
    return res;
  });
});

/**
 * Create a client.
 *
 * @returns {Primacron} client
 * @api private
 */
Primacron.client = function client() {
  var primacron = new Primacron(new EventEmitter(), {});

  return primacron.Socket;
};

//
// Expose the Server
//
module.exports = Primacron;
<commit_msg>[minor] Added concurrent property<commit_after>'use strict';

var EventEmitter = require('events').EventEmitter
  , Primus = require('primus')
  , fuse = require('fusing')
  , path = require('path');

/**
 * Create a new Primacron server.
 *
 * @constructor
 * @param {Server} server HTTP server.
 * @param {Object} options Optional configuration.
 * @api private
 */
function Primacron(server, options) {
  if (!(this instanceof Primacron)) return new Primacron(server, options);

  options = this.configurable(options || server);

  //
  // In Node, all servers inherit from the EventEmitter or net.Server. This is
  // gives us a some what reliable check to see if we need to create a server.
  // By allowing EventEmitters we can also compile client code without creating
  // an actual HTTP server.
  //
  if (!(server instanceof EventEmitter)) {
    server = require('create-server')(server || options);
  }

  var listening = !!server.listeners('listening').length;

  this.fuse([server, options]);

  this.use('fortress maximus', require('fortress-maximus'));
  this.use('omega supreme', require('omega-supreme'));
  this.use('emit', require('primus-emit/broadcast'));
  this.use('metroplex', require('metroplex'));
  this.use('mirage', require('mirage'));

  //
  // If the provided options tell the create-server to automatically start
  // listening on the server we need to automatically call the .listen method so
  // we can assign the correct listeners.
  //
  if (listening) {
    this.listen();
  }
}

fuse(Primacron, Primus);

/**
 * Return a pre-configured configuration for Primus.
 *
 * @param {Object} options Given optional options.
 * @returns {Object} Pre configured objects.
 * @api private
 */
Primacron.readable('configurable', function configurable(options) {
  if ('object' !== typeof options) options = {};

  var pathname = options.pathname || '/primacron';

  return this.merge({
    url: path.resolve(pathname, './omega/supreme'),
    transformer: 'engine.io',
    namespace: 'primacron',
    pathname: pathname,
    fortress: 'primus',
    concurrently: 10,
    parser: 'JSON'
  }, options);
});

/**
 * This argument accepts what ever you want to send to a regular server.listen
 * method.
 *
 * @api public
 */
Primacron.readable('listen', function listen() {
  var listening = !!this.server.listeners('listening').length;

  //
  // Proxy the events of the HTTP server to our own Primacron instance.
  //
  this.server.on('listening', this.emits('listening'));
  this.server.on('error', this.emits('error'));
  this.server.on('close', this.emits('close'));

  //
  // Proxy all arguments to the server if we're not already listening
  //
  if (!listening && this.server.listen) {
    this.server.listen.apply(this.server, arguments);
  }
});

//
// Add missing methods of a regular HTTP server that we can proxy from our
// internal `this.server` instance.
//
['address', 'getConnections'].forEach(function missing(method) {
  Primacron.readable(method, function proxy() {
    var res = this.server[method].apply(this.server, arguments);

    //
    // Figure out which kind of value we should return. If this was a chaining
    // method on the server we should just return Primacron instead. If it's not
    // the server that is returned we should return the value.
    //
    if (res === this.server) return this;
    return res;
  });
});

/**
 * Create a client.
 *
 * @returns {Primacron} client
 * @api private
 */
Primacron.client = function client() {
  var primacron = new Primacron(new EventEmitter(), {});

  return primacron.Socket;
};

//
// Expose the Server
//
module.exports = Primacron;
<|endoftext|>


<commit_before>/**
 * Code based on page.js
 * https://github.com/visionmedia/page.js
 */

'use strict';

function getParams(href) {
  var params = {};
  var splitHref = href.split('?');

  if (splitHref[1]) {
    splitHref[1].split('&')
      .forEach(function(param) {
        var i = param.indexOf('=');

        if (i === -1) {
          params[window.decodeURIComponent(param)] = '';
          return;
        }

        var name = window.decodeURIComponent(param.substr(0, i));
        var value = window.decodeURIComponent(param.substr(i + 1));
        params[name] = value
      });
  }

  return params;
}

var defaultOpts = {};

module.exports = function(router, opts, cb) {
  var clickEvent = document.ontouchstart ? 'touchstart' : 'click';

  function which(e) {
    e = e || window.event;
    return null === e.which ? e.button : e.which;
  }

  function onclick(e) {
    if (1 !== which(e)) return;

    if (e.metaKey || e.ctrlKey || e.shiftKey) return;
    if (e.defaultPrevented) return;

    // ensure link
    var el = e.target;
    while (el && 'A' !== el.nodeName) el = el.parentNode;
    if (!el || 'A' !== el.nodeName) return;

    // Ignore if tag has
    // 1. "download" attribute
    // 2. rel="external" attribute
    if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return;

    // check target
    if (el.target) return;

    var toRouteState = router.matchUrl(el.href);
    if (toRouteState) {
      e.preventDefault();
      var name = toRouteState.name;
      var params = getParams(el.href);

      var finalOpts;
      if (typeof opts === 'function') {
        finalOpts = opts(name, params);
      } else {
        finalOpts = opts;
      }

      if (!finalOpts) {
        finalOpts = defaultOpts;
      }

      router.navigate(name, params, finalOpts, cb);
    }
  }

  document.addEventListener(clickEvent, onclick, false);

  return function stop() {
    document.removeEventListener(clickEvent, onclick, false);
  }
};
<commit_msg>refactor<commit_after>/**
 * Code based on page.js
 * https://github.com/visionmedia/page.js
 */

'use strict';

module.exports = function(router, opts, cb) {
  var clickEvent = document.ontouchstart ? 'touchstart' : 'click';

  function which(e) {
    e = e || window.event;
    return null === e.which ? e.button : e.which;
  }

  function getParams(href) {
    var params = {};
    var splitHref = href.split('?');

    if (splitHref[1]) {
      splitHref[1].split('&')
        .forEach(function(param) {
          var i = param.indexOf('=');

          if (i === -1) {
            params[window.decodeURIComponent(param)] = '';
            return;
          }

          var name = window.decodeURIComponent(param.substr(0, i));
          var value = window.decodeURIComponent(param.substr(i + 1));
          params[name] = value
        });
    }

    return params;
  }

  function onclick(e) {
    if (1 !== which(e)) return;

    if (e.metaKey || e.ctrlKey || e.shiftKey) return;
    if (e.defaultPrevented) return;

    // ensure link
    var el = e.target;
    while (el && 'A' !== el.nodeName) el = el.parentNode;
    if (!el || 'A' !== el.nodeName) return;

    // Ignore if tag has
    // 1. "download" attribute
    // 2. rel="external" attribute
    if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return;

    // check target
    if (el.target) return;

    var toRouteState = router.matchUrl(el.href);
    if (toRouteState) {
      e.preventDefault();
      var name = toRouteState.name;
      var params = getParams(el.href);

      var finalOpts;
      if (typeof opts === 'function') {
        finalOpts = opts(name, params);
      } else {
        finalOpts = opts;
      }

      router.navigate(name, params, finalOpts, cb);
    }
  }

  document.addEventListener(clickEvent, onclick, false);

  return function stop() {
    document.removeEventListener(clickEvent, onclick, false);
  }
};
<|endoftext|>


<commit_before>//
// index.js: Run the Bitlash Commander server
//
//	Copyright 2012 Bill Roy (MIT License; see LICENSE file)
//

var opt = require('optimist');
var url = require('url');
var argv = opt.usage('Usage: $0 [flags]')
	.alias('p', 'port')
	.describe('p', 'port for the http server')
	.alias('s', 'serialport')
	.describe('s', 'port for usbserial arduino connection')
	.alias('r', 'redis')
	.describe('r', 'redis server url in redis-url format')
	.argv;

if (argv.help) {
	opt.showHelp();
	process.exit();
}

var port;
var heroku;
if (argv.port) port = argv.port;
else if (process && process.env && process.env.PORT) {
	heroku = true;
	port = process.env.PORT;
}
else port = 3000;

var express = require('express');
var app = express();
var http = require('http')
var server = http.createServer(app)
var io = require('socket.io').listen(server);

app.configure(function () {
	app.use(express.logger());
	app.use(express.bodyParser());
	app.use(express.static(__dirname + '/public'));
});

app.get('/', function(req, res) {
	res.sendfile(__dirname + '/public/index.html');
});

app.get('/data', function(req, res) {
	res.send(data_cache);
});

app.get('/d3/:id', function(req, res) {
	//console.log('D3Get:', req.params.id, data_cache);
	var output = []; 			// array of {time:23432, value:dsjklfjsd}
	if (data_cache[req.params.id]) {
		var data = data_cache[req.params.id];
		for (var i=0; i<data.length; i++) {
			var point = {time: data[i].time};
			point[req.params.id] = data[i].value;
			output.push(point);
		}
	}
	else if (req.params.id == '$random') {
		for (var i=0; i<100; i++) {
			var point = {time: i};
			point[req.params.id] = Math.floor(Math.random()*1000);
			output.push(point);
		}
	}
	//console.log('D3Out:', output);
	res.send(output);
});

server.listen(port);
console.log('Listening on port ', port);

//////////
//
//	Initialize Bitlash
//
var Bitlash = require('./lib/bitlash.js');
var bitlash = new Bitlash.Bitlash({
		debug: true,
		echo: true,
		port: argv.serialport,
		json_callback: broadcastJSONUpdate
	}, function (readytext) {
		console.log('Ready:', readytext);
	});

//////////
//
//	Initialize the Redis Store for Socket.io, if needed
//
var redis_url = process.env.REDISTOGO_URL || argv.redis || undefined;
var redis, redis_pub, redis_sub, redis_client, RedisStore, RedisURL;
if (redis_url) {
	var parsed_url = url.parse(redis_url);
	console.log('Connecting to Redis at:', redis_url, parsed_url, parsed_url.auth.split(":")[1]);
	redis = require('redis');
	redis_pub = redis.createClient(parsed_url.port, parsed_url.hostname);
	redis_sub = redis.createClient(parsed_url.port, parsed_url.hostname);
	redis_client = redis.createClient(parsed_url.port, parsed_url.hostname);

	redis_pub.auth(parsed_url.auth.split(':')[1]);
	redis_sub.auth(parsed_url.auth.split(':')[1]);
	redis_client.auth(parsed_url.auth.split(':')[1]);

	RedisStore = require('socket.io/lib/stores/redis');
}

//////////
//
//	Initialize Socket.io
//
// for heroku,
// per https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
if (1 || heroku) {
	io.configure(function () {
		io.set('transports', ['xhr-polling']);
		io.set('polling duration', 10);
		if (RedisStore) {
			console.log('Starting RedisStore');
			io.set('store', new RedisStore({redis: redis, redisPub: redis_pub, redisSub: redis_sub, redisClient: redis_client}));
		}
	});
}
io.set('log level', 1);

// BUG: leaks control id's across rooms
// BUG: confuses control ID's across rooms
var data_cache = {};		// caches updates per id by time

function addCache(id, value) {
	if (!data_cache[id]) data_cache[id] = [];	// array of objects: {id:<id>, t:<time>, value:<value>}
	data_cache[id].push({
		time: new Date().getTime(),
		value: value
	});
}

// for heroku,
// per https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
if (heroku) {
	io.configure(function () {
		io.set("transports", ["xhr-polling"]);
		io.set("polling duration", 10);
	});
}
io.set('log level', 1);

function executeBitlash(data) {
	console.log('Exec:', data, typeof data, bitlash.ready);
	if (bitlash.ready) {
		bitlash.exec(data.cmd + '\n', function(reply) {
			reply = reply.trim();
			addCache(data.id, reply);
			console.log('sending update:', reply);
			data.value = reply;
			io.sockets.emit('update', data);
		});
	}
	else if (heroku) io.sockets.emit('rexec', data);
	else console.log('Busy, dropping:', data);
}

io.sockets.on('connection', function (socket) {
	console.log('Client connected via', socket.transport);
	socket.on('exec', executeBitlash);
	socket.on('rexec', executeBitlash);
	socket.on('update', function(data) {
		console.log('Update????:', data);
		socket.broadcast.emit('update', data);	// everyone but requester
	});
	socket.on('sync', function(data) {
		var response = [];
		for (var id in data_cache) {
			var datalist = data_cache[id];
			if (datalist.length) response.push({id:id, value:datalist[datalist.length-1].value});
		}
		//console.log('Sync:', response);
		// only to requester
		socket.emit('update', response);
	});
	socket.on('ping', function(data) {
		socket.emit('pong', data);
	});
});

function broadcastJSONUpdate(data) {
	io.sockets.emit('update', data);
}

<commit_msg>rexec bugs<commit_after>//
// index.js: Run the Bitlash Commander server
//
//	Copyright 2012 Bill Roy (MIT License; see LICENSE file)
//

var opt = require('optimist');
var url = require('url');
var argv = opt.usage('Usage: $0 [flags]')
	.alias('p', 'port')
	.describe('p', 'port for the http server')
	.alias('s', 'serialport')
	.describe('s', 'port for usbserial arduino connection')
	.alias('r', 'redis')
	.describe('r', 'redis server url in redis-url format')
	.argv;

if (argv.help) {
	opt.showHelp();
	process.exit();
}

var port;
var heroku;
if (argv.port) port = argv.port;
else if (process && process.env && process.env.PORT) {
	heroku = true;
	port = process.env.PORT;
}
else port = 3000;

var express = require('express');
var app = express();
var http = require('http')
var server = http.createServer(app)
var io = require('socket.io').listen(server);

app.configure(function () {
	app.use(express.logger());
	app.use(express.bodyParser());
	app.use(express.static(__dirname + '/public'));
});

app.get('/', function(req, res) {
	res.sendfile(__dirname + '/public/index.html');
});

app.get('/data', function(req, res) {
	res.send(data_cache);
});

app.get('/d3/:id', function(req, res) {
	//console.log('D3Get:', req.params.id, data_cache);
	var output = []; 			// array of {time:23432, value:dsjklfjsd}
	if (data_cache[req.params.id]) {
		var data = data_cache[req.params.id];
		for (var i=0; i<data.length; i++) {
			var point = {time: data[i].time};
			point[req.params.id] = data[i].value;
			output.push(point);
		}
	}
	else if (req.params.id == '$random') {
		for (var i=0; i<100; i++) {
			var point = {time: i};
			point[req.params.id] = Math.floor(Math.random()*1000);
			output.push(point);
		}
	}
	//console.log('D3Out:', output);
	res.send(output);
});

server.listen(port);
console.log('Listening on port ', port);

//////////
//
//	Initialize Bitlash
//
var Bitlash = require('./lib/bitlash.js');
var bitlash = new Bitlash.Bitlash({
		debug: true,
		echo: true,
		port: argv.serialport,
		json_callback: broadcastJSONUpdate
	}, function (readytext) {
		console.log('Ready:', readytext);
	});

//////////
//
//	Initialize the Redis Store for Socket.io, if needed
//
var redis_url = process.env.REDISTOGO_URL || argv.redis || undefined;
var redis, redis_pub, redis_sub, redis_client, RedisStore, RedisURL;
if (redis_url) {
	var parsed_url = url.parse(redis_url);
	console.log('Connecting to Redis at:', redis_url, parsed_url, parsed_url.auth.split(":")[1]);
	redis = require('redis');
	redis_pub = redis.createClient(parsed_url.port, parsed_url.hostname);
	redis_sub = redis.createClient(parsed_url.port, parsed_url.hostname);
	redis_client = redis.createClient(parsed_url.port, parsed_url.hostname);

	redis_pub.auth(parsed_url.auth.split(':')[1]);
	redis_sub.auth(parsed_url.auth.split(':')[1]);
	redis_client.auth(parsed_url.auth.split(':')[1]);

	RedisStore = require('socket.io/lib/stores/redis');
}

//////////
//
//	Initialize Socket.io
//
// for heroku,
// per https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
if (1 || heroku) {
	io.configure(function () {
		io.set('transports', ['xhr-polling']);
		io.set('polling duration', 10);
		if (RedisStore) {
			console.log('Starting RedisStore');
			io.set('store', new RedisStore({redis: redis, redisPub: redis_pub, redisSub: redis_sub, redisClient: redis_client}));
		}
	});
}
io.set('log level', 1);

// BUG: leaks control id's across rooms
// BUG: confuses control ID's across rooms
var data_cache = {};		// caches updates per id by time

function addCache(id, value) {
	if (!data_cache[id]) data_cache[id] = [];	// array of objects: {id:<id>, t:<time>, value:<value>}
	data_cache[id].push({
		time: new Date().getTime(),
		value: value
	});
}

// for heroku,
// per https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
if (heroku) {
	io.configure(function () {
		io.set("transports", ["xhr-polling"]);
		io.set("polling duration", 10);
	});
}
io.set('log level', 1);

function executeBitlash(data) {
	console.log('Exec:', data, typeof data, bitlash.ready);
	if (bitlash.ready) {
		bitlash.exec(data.cmd + '\n', function(reply) {
			reply = reply.trim();
			addCache(data.id, reply);
			console.log('sending update:', reply);
			data.value = reply;
			io.sockets.emit('update', data);
		});
	}
	else if (heroku) {
		console.log('Rexec:', data);
		io.sockets.emit('rexec', data);
	}
	else console.log('Busy, dropping:', data);
}

io.sockets.on('connection', function (socket) {
	console.log('Client connected via', socket.transport);
	socket.on('exec', executeBitlash);
	socket.on('rexec', function(data) {
		console.log('rexec:', data);
		executeBitlash(data)
	});
	socket.on('update', function(data) {
		console.log('Update????:', data);
		socket.broadcast.emit('update', data);	// everyone but requester
	});
	socket.on('sync', function(data) {
		var response = [];
		for (var id in data_cache) {
			var datalist = data_cache[id];
			if (datalist.length) response.push({id:id, value:datalist[datalist.length-1].value});
		}
		//console.log('Sync:', response);
		// only to requester
		socket.emit('update', response);
	});
	socket.on('ping', function(data) {
		socket.emit('pong', data);
	});
});

function broadcastJSONUpdate(data) {
	io.sockets.emit('update', data);
}

<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

var path = require('path')
 , defaults = require('lodash/object/defaults')
 , mergeTrees = require('broccoli-merge-trees')
 , Funnel = require('broccoli-funnel')
 , ScssLinter = require('broccoli-scss-linter');

module.exports = {
  name: 'ember-cli-scss-lint',

  /*
   * Merge scss-lint configuration
   * options from the consuming
   * application.
   *
   * @method included
   *
   * @param {Object} app
   *   An EmberApp instance.
   */
  included: function(app) {
    this._super.included.call(this, app);

    // Merge the consuming application's options with the default options.
    this.scssLintOptions = defaults(app.options.scssLintOptions || {}, {
      config: path.join(app.project.root, '.sass-lint.yml'),
      testGenerator: 'qunit'
    });

    this.app = app;
  },

  /*
   * Lint the application's styles
   * tree and report errors to the
   * user.
   *
   * @method lintTree
   *
   * @param {String} treeType
   *   Either 'app', 'tests', 'templates', or 'addon'.
   *
   * @return {Object}
   *   Tree to be merged.
   */
  lintTree: function(treeType, tree) {
    if (treeType === 'app') {
      var trees = [this.app.trees.app];

      // Push any custom paths onto the trees array
      // to be linted.
      if (this.scssLintOptions.includePaths) {
        trees.push.apply(trees, this.scssLintOptions.includePaths);
      }

      var linted = trees.map(function(tree) {
        let filteredTreeToBeLinted = new Funnel(tree, { exclude: '**/*.js' });
        return new ScssLinter(mergeTrees([tree]), this.scssLintOptions);
      }, this);

      return mergeTrees(linted);
    }
  }
};
<commit_msg>Pass filtered tree to ScssLinter<commit_after>/* jshint node: true */
'use strict';

var path = require('path')
 , defaults = require('lodash/object/defaults')
 , mergeTrees = require('broccoli-merge-trees')
 , Funnel = require('broccoli-funnel')
 , ScssLinter = require('broccoli-scss-linter');

module.exports = {
  name: 'ember-cli-scss-lint',

  /*
   * Merge scss-lint configuration
   * options from the consuming
   * application.
   *
   * @method included
   *
   * @param {Object} app
   *   An EmberApp instance.
   */
  included: function(app) {
    this._super.included.call(this, app);

    // Merge the consuming application's options with the default options.
    this.scssLintOptions = defaults(app.options.scssLintOptions || {}, {
      config: path.join(app.project.root, '.sass-lint.yml'),
      testGenerator: 'qunit'
    });

    this.app = app;
  },

  /*
   * Lint the application's styles
   * tree and report errors to the
   * user.
   *
   * @method lintTree
   *
   * @param {String} treeType
   *   Either 'app', 'tests', 'templates', or 'addon'.
   *
   * @return {Object}
   *   Tree to be merged.
   */
  lintTree: function(treeType, tree) {
    if (treeType === 'app') {
      var trees = [this.app.trees.app];

      // Push any custom paths onto the trees array
      // to be linted.
      if (this.scssLintOptions.includePaths) {
        trees.push.apply(trees, this.scssLintOptions.includePaths);
      }

      var linted = trees.map(function(tree) {
        let filteredTreeToBeLinted = new Funnel(tree, { exclude: '**/*.js' });
        return new ScssLinter(mergeTrees([filteredTreeToBeLinted]), this.scssLintOptions);
      }, this);

      return mergeTrees(linted);
    }
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
*  Cek Resi CLI
*  (c) 2016 Ipan Ardian
*
*  A command line app to tracking AWB number
*  For details, see the web site: https://github.com/ipanardian/cekresi-cli
*  The MIT License
*/
'use strict'

const VERSION = 'v1.0.6'

if (process.argv.length <= 2) {
    console.log("Usage: cekresi-cli AWB_NUMBER")
    process.exit(-1)
}

var param = process.argv[2]
if (param == '--version') {
	console.log(VERSION)
	process.exit(-1)
}
else if (param.length < 10 || !param.match(/^[a-z0-9]+$/i)) {
	console.log('Invalid AWB Number')
	process.exit(-1)
}

var Horseman = require('node-horseman')
var cheerio = require('cheerio')
var tabletojson = require('tabletojson')
var Table = require('cli-table')
var colors = require('colors')
var Spinner = require('cli-spinner').Spinner
var spinner = new Spinner('Checking... %s ')
spinner.setSpinnerString(7)
spinner.start()

var horseman = new Horseman()
	horseman
	.userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.3")
	.on('error', function(message, trace) {
		console.log(message)
	})
	.on('timeout', function(message) {
		console.log('Timeout\n')
	})
	.open('http://cekresi.com/?noresi='+param)
	.click("#cekresi")
	.waitFor(function(selector) {
		return $(selector).is(':visible')
	}, '#results', true)
	.evaluate(function() {
		return document.querySelector('#results').innerHTML
	})
	.then(function(result) {
		cekresi.load(result).parse()
	})
	.finally(function() {
	    horseman.close()
	    spinner.stop()
	    return
	})

var cekresi = {
	result: null,
	tablesAsJson: null,
	infoExpedisi: null,
	el: null,
	checkResult() {
		return (this.result != '' && this.infoExpedisi != '' ? true:false)
	},
	load(res) {
		this.parseHTML(res)
		this.result = res
		this.infoExpedisi = this.el('h3.top_title').text()
		return this
	},
	parseHTML(html) {
		this.el = cheerio.load(html)
	},
	parseDeliveryInfo() {
		var deliveryTable = new Table()
		for (let tr in this.tablesAsJson[0]){
			let row = []
			for (let td in this.tablesAsJson[0][tr]) {
				row.push(this.tablesAsJson[0][tr][td])
			}
			deliveryTable.push(row)
		}
		print(deliveryTable.toString())
	},
	parseActivityInfo() {
		let header = []
		let body = []
		for (let i = 1; i <= this.tablesAsJson.length - 1; i++) {
			for (let tr in this.tablesAsJson[i]) {
				let row = []
				for (let td in this.tablesAsJson[i][tr]) {
					if (body.length < 1)
						header.push(td)

					row.push(this.tablesAsJson[i][tr][td])
				}
				body.push(row)
			}
		}
		var activityTable = new Table({head: header})
		for (let item in body) {
			activityTable.push(body[item])
		}
		print(activityTable.toString())
	},
	parse() {
		if (this.checkResult()) {
			this.tablesAsJson = tabletojson.convert(this.result)
			print('\n==================== '+ this.infoExpedisi.yellow +' =========================\n')
			this.parseDeliveryInfo()
			print('')
			this.parseActivityInfo()
		}
		else {
			print('\nMaaf no resi yang anda masukkan sementara tidak dapat kami proses'.yellow)
		}
		print('\nFor details: https://github.com/ipanardian/cekresi-cli')
	}
}

function print(msg) {
	console.log(msg)
}<commit_msg>Fixed wahana AWB length<commit_after>#!/usr/bin/env node
/**
*  Cek Resi CLI
*  (c) 2016 Ipan Ardian
*
*  A command line app to tracking AWB number
*  For details, see the web site: https://github.com/ipanardian/cekresi-cli
*  The MIT License
*/
'use strict'

const VERSION = 'v1.0.6'

if (process.argv.length <= 2) {
    console.log("Usage: cekresi-cli AWB_NUMBER")
    process.exit(-1)
}

var param = process.argv[2]
if (param == '--version') {
	console.log(VERSION)
	process.exit(-1)
}
else if (param.length < 5 || !param.match(/^[a-z0-9]+$/i)) {
	console.log('Invalid AWB Number')
	process.exit(-1)
}

var Horseman = require('node-horseman')
var cheerio = require('cheerio')
var tabletojson = require('tabletojson')
var Table = require('cli-table')
var colors = require('colors')
var Spinner = require('cli-spinner').Spinner
var spinner = new Spinner('Checking... %s ')
spinner.setSpinnerString(7)
spinner.start()

var horseman = new Horseman()
	horseman
	.userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.3")
	.on('error', function(message, trace) {
		console.log(message)
	})
	.on('timeout', function(message) {
		console.log('Timeout\n')
	})
	.open('http://cekresi.com/?noresi='+param)
	.click("#cekresi")
	.waitFor(function(selector) {
		return $(selector).is(':visible')
	}, '#results', true)
	.evaluate(function() {
		return document.querySelector('#results').innerHTML
	})
	.then(function(result) {
		cekresi.load(result).parse()
	})
	.finally(function() {
	    horseman.close()
	    spinner.stop()
	    return
	})

var cekresi = {
	result: null,
	tablesAsJson: null,
	infoExpedisi: null,
	el: null,
	checkResult() {
		return (this.result != '' && this.infoExpedisi != '' ? true:false)
	},
	load(res) {
		this.parseHTML(res)
		this.result = res
		this.infoExpedisi = this.el('h3.top_title').text()
		return this
	},
	parseHTML(html) {
		this.el = cheerio.load(html)
	},
	parseDeliveryInfo() {
		var deliveryTable = new Table()
		for (let tr in this.tablesAsJson[0]){
			let row = []
			for (let td in this.tablesAsJson[0][tr]) {
				row.push(this.tablesAsJson[0][tr][td])
			}
			deliveryTable.push(row)
		}
		print(deliveryTable.toString())
	},
	parseActivityInfo() {
		let header = []
		let body = []
		for (let i = 1; i <= this.tablesAsJson.length - 1; i++) {
			for (let tr in this.tablesAsJson[i]) {
				let row = []
				for (let td in this.tablesAsJson[i][tr]) {
					if (body.length < 1)
						header.push(td)

					row.push(this.tablesAsJson[i][tr][td])
				}
				body.push(row)
			}
		}
		var activityTable = new Table({head: header})
		for (let item in body) {
			activityTable.push(body[item])
		}
		print(activityTable.toString())
	},
	parse() {
		if (this.checkResult()) {
			this.tablesAsJson = tabletojson.convert(this.result)
			print('\n==================== '+ this.infoExpedisi.yellow +' =========================\n')
			this.parseDeliveryInfo()
			print('')
			this.parseActivityInfo()
		}
		else {
			print('\nMaaf no resi yang anda masukkan sementara tidak dapat kami proses'.yellow)
		}
		print('\nFor details: https://github.com/ipanardian/cekresi-cli')
	}
}

function print(msg) {
	console.log(msg)
}<|endoftext|>


<commit_before>'use strict';
const {URL} = require('url');
const urlRegex = require('url-regex');
const normalizeUrl = require('normalize-url');

function getUrlsFromQueryParams(url) {
	const ret = new Set();
	const {searchParams} = (new URL(url));

	for (const [, value] of searchParams) {
		if (urlRegex({exact: true}).test(value)) {
			ret.add(value);
		}
	}

	return ret;
}

module.exports = (text, options = {}) => {
	if (typeof options.exclude !== 'undefined' && !Array.isArray(options.exclude)) {
		throw new TypeError('The `exclude` option must be an array');
	}

	const ret = new Set();

	const add = url => {
		ret.add(normalizeUrl(url.trim().replace(/\.+$/, ''), options));
	};

	const urls = text.match(urlRegex()) || [];
	for (const url of urls) {
		add(url);

		if (options.extractFromQueryString) {
			for (const qsUrl of getUrlsFromQueryParams(url)) {
				add(qsUrl);
			}
		}
	}

	for (const excludedItem of options.exclude || []) {
		for (const item of ret) {
			const regex = new RegExp(excludedItem);
			if (regex.test(item)) {
				ret.delete(item);
				break;
			}
		}
	}

	return ret;
};
<commit_msg>Ignore invalid URLs<commit_after>'use strict';
const {URL} = require('url');
const urlRegex = require('url-regex');
const normalizeUrl = require('normalize-url');

function getUrlsFromQueryParams(url) {
	const ret = new Set();
	const {searchParams} = (new URL(url));

	for (const [, value] of searchParams) {
		if (urlRegex({exact: true}).test(value)) {
			ret.add(value);
		}
	}

	return ret;
}

module.exports = (text, options = {}) => {
	if (typeof options.exclude !== 'undefined' && !Array.isArray(options.exclude)) {
		throw new TypeError('The `exclude` option must be an array');
	}

	const ret = new Set();

	const add = url => {
		try {
			ret.add(normalizeUrl(url.trim().replace(/\.+$/, ''), options));
		} catch (_) {}
	};

	const urls = text.match(urlRegex()) || [];
	for (const url of urls) {
		add(url);

		if (options.extractFromQueryString) {
			for (const qsUrl of getUrlsFromQueryParams(url)) {
				add(qsUrl);
			}
		}
	}

	for (const excludedItem of options.exclude || []) {
		for (const item of ret) {
			const regex = new RegExp(excludedItem);
			if (regex.test(item)) {
				ret.delete(item);
				break;
			}
		}
	}

	return ret;
};
<|endoftext|>


<commit_before>/* global node: true */
module.exports = {
  name: 'ember-pop-over',
  options: {
    nodeAssets: {
      'dom-ruler': {
        srcDir: 'dist/amd',
        import: ['dom-ruler.js']
      }
    }
  },

  included: function (app) {
    this._super.included.apply(this, arguments);
    app.import('vendor/dom-ruler/dom-ruler.js', {
      exports: {
        'dom-ruler': ['default']
      }
    });
    app.import("vendor/styles/ember-popup-menu.css");
  }
};
<commit_msg>Remove unnecessary node: true<commit_after>module.exports = {
  name: 'ember-pop-over',
  options: {
    nodeAssets: {
      'dom-ruler': {
        srcDir: 'dist/amd',
        import: ['dom-ruler.js']
      }
    }
  },

  included: function (app) {
    this._super.included.apply(this, arguments);
    app.import('vendor/dom-ruler/dom-ruler.js', {
      exports: {
        'dom-ruler': ['default']
      }
    });
    app.import("vendor/styles/ember-popup-menu.css");
  }
};
<|endoftext|>


<commit_before>var concat = require('concat-stream')
var http = require('http')
var fs = require('fs')
var url = require('url')
var async = require('async')

// local requires
var checkPR = require('./prcheck.js')
var checkCollab = require('./collabcheck.js')
var mergePR = require('./merge.js')

// q to slow it down enough for the GitHub API
var q = async.queue(function que (pullreq, callback) {
  console.log(new Date(), 'QUEUE', pullreq.number)
  mergePR(pullreq, function donePR (err, message) {
    if (err) console.log(new Date(), message, err)
    // setTimeout(function() { callback(err) }, 1000)
    // what is this cb doing with err
    callback(err)
  })
}, 1)

console.log('QUEUE LENGTH', q.length())

q.drain = function drain () { console.log('Queue drain') }

module.exports = function (onHook) {
  var server = http.createServer(handler)

  // handler routes the requests to RR
  // to the appropriate places
  function handler (req, res) {
    console.log('>>>>>', new Date(), req.method, req.url)

    // End point to latest data
    if (req.url === ('/data')) {
      console.log(new Date(), 'Request for data')
      return fs.readFile(process.env['CONTRIBUTORS'], function (err, data) {
        if (err) return console.log(new Date(), err)
        res.statusCode = 200
        res.end(JSON.stringify(JSON.parse(data.toString())))
      })
    }

    // When RR gets a push from email when added as collab
    // Email from GitHub -> cloudmail.in -> here
    if (req.url === '/push') {
      return handleEmail(req, res)
    }

    // When a PR is made to patchwork repo
    // Comes from a GitHub webhook Patchwork repo
    if (req.url.match('/orderin')) {
      return getPR(req, res)
    }

    // When Git-it verifies user made a PR
    // Comes from verify step in Git-it challenge #10
    var queryURL
    var username
    if (req.url.match('/pr')) {
      queryURL = url.parse(req.url, true)
      username = queryURL.query.username
      return checkPR(username, function (err, pr) {
        // where does this res come from? The req.
        prStatus(res, err, pr)
      })
    }

    // When Git-it verifies user added RR as collab
    // Comes from verify step in Git-it challenge #8
    if (req.url.match('/collab')) {
      queryURL = url.parse(req.url, true)
      username = queryURL.query.username
      return checkCollab(username, function (err, collab) {
        collabStatus(res, err, collab)
      })
    }

    // When any other request goes to reporobot.jlord.us
    res.statusCode = 404
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 404,
      message: 'not_found'
    }, true, 2))
  }

  function handleEmail (req, res) {
    req.pipe(concat(function (buff) {
      try {
        var emailObj = JSON.parse(buff)
      } catch (e) {
        return console.log(new Date(), 'Error parsing email JSON')
      }

      if (onHook) {
        onHook(emailObj, function (err, message) {
          if (err) console.log(new Date(), message, err)
        })
      }
    }))

    res.statusCode = 200
    res.end('Thank you.')
  }

  function getPR (req, res) {
    req.pipe(concat(function (buff) {
      var pullreq = JSON.parse(buff)

      // Check if it's a closed PR
      if (pullreq.action && pullreq.action === 'closed') {
        console.log('SKIPPING: CLOSED PULL REQUEST')
      } else {
        // send open PR to the queue
        q.push(pullreq, function (err, message) {
          if (err) console.log(new Date(), message, err)
          console.log(new Date(), pullreq.number, message, 'Finished PR')
        })
      }

      res.statusCode = 200
      res.setHeader('content-type', 'application/json')
      res.end()
    }))
  }

  function prStatus (res, err, pr) {
    if (err) {
      console.log(err)
      res.statusCode = 500
      res.end(JSON.stringify({error: err}))
      return
    }
    res.statusCode = 200
    res.end(JSON.stringify({
      pr: pr
    }, true, 2))
  }

  function collabStatus (res, err, collab) {
    if (err) {
      console.log(err)
      res.statusCode = 500
      res.end(JSON.stringify({error: err}))
      return
    }
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      collab: collab
    }, true, 2))
  }

  // does this ever get called?
  // function mergedPr (res, err) {
  //   if (err) {
  //     console.log(err)
  //     res.statusCode = 500
  //     res.end(JSON.stringify({ error: err }))
  //     return
  //   }
  //   res.statusCode = 200
  //   res.setHeader('content-type', 'application/json')
  //   res.end(JSON.stringify({
  //     merged: true
  //   }, true, 2))
  // }

  return server
}
<commit_msg>Print out bad email payloads<commit_after>var concat = require('concat-stream')
var http = require('http')
var fs = require('fs')
var url = require('url')
var async = require('async')

// local requires
var checkPR = require('./prcheck.js')
var checkCollab = require('./collabcheck.js')
var mergePR = require('./merge.js')

// q to slow it down enough for the GitHub API
var q = async.queue(function que (pullreq, callback) {
  console.log(new Date(), 'QUEUE', pullreq.number)
  mergePR(pullreq, function donePR (err, message) {
    if (err) console.log(new Date(), message, err)
    // setTimeout(function() { callback(err) }, 1000)
    // what is this cb doing with err
    callback(err)
  })
}, 1)

console.log('QUEUE LENGTH', q.length())

q.drain = function drain () { console.log('Queue drain') }

module.exports = function (onHook) {
  var server = http.createServer(handler)

  // handler routes the requests to RR
  // to the appropriate places
  function handler (req, res) {
    console.log('>>>>>', new Date(), req.method, req.url)

    // End point to latest data
    if (req.url === ('/data')) {
      console.log(new Date(), 'Request for data')
      return fs.readFile(process.env['CONTRIBUTORS'], function (err, data) {
        if (err) return console.log(new Date(), err)
        res.statusCode = 200
        res.end(JSON.stringify(JSON.parse(data.toString())))
      })
    }

    // When RR gets a push from email when added as collab
    // Email from GitHub -> cloudmail.in -> here
    if (req.url === '/push') {
      return handleEmail(req, res)
    }

    // When a PR is made to patchwork repo
    // Comes from a GitHub webhook Patchwork repo
    if (req.url.match('/orderin')) {
      return getPR(req, res)
    }

    // When Git-it verifies user made a PR
    // Comes from verify step in Git-it challenge #10
    var queryURL
    var username
    if (req.url.match('/pr')) {
      queryURL = url.parse(req.url, true)
      username = queryURL.query.username
      return checkPR(username, function (err, pr) {
        // where does this res come from? The req.
        prStatus(res, err, pr)
      })
    }

    // When Git-it verifies user added RR as collab
    // Comes from verify step in Git-it challenge #8
    if (req.url.match('/collab')) {
      queryURL = url.parse(req.url, true)
      username = queryURL.query.username
      return checkCollab(username, function (err, collab) {
        collabStatus(res, err, collab)
      })
    }

    // When any other request goes to reporobot.jlord.us
    res.statusCode = 404
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 404,
      message: 'not_found'
    }, true, 2))
  }

  function handleEmail (req, res) {
    req.pipe(concat(function (buff) {
      try {
        var emailObj = JSON.parse(buff)
      } catch (e) {
        return console.log(new Date(), 'Error parsing email JSON', [buff.toString()])
      }

      if (onHook) {
        onHook(emailObj, function (err, message) {
          if (err) console.log(new Date(), message, err)
        })
      }
    }))

    res.statusCode = 200
    res.end('Thank you.')
  }

  function getPR (req, res) {
    req.pipe(concat(function (buff) {
      var pullreq = JSON.parse(buff)

      // Check if it's a closed PR
      if (pullreq.action && pullreq.action === 'closed') {
        console.log('SKIPPING: CLOSED PULL REQUEST')
      } else {
        // send open PR to the queue
        q.push(pullreq, function (err, message) {
          if (err) console.log(new Date(), message, err)
          console.log(new Date(), pullreq.number, message, 'Finished PR')
        })
      }

      res.statusCode = 200
      res.setHeader('content-type', 'application/json')
      res.end()
    }))
  }

  function prStatus (res, err, pr) {
    if (err) {
      console.log(err)
      res.statusCode = 500
      res.end(JSON.stringify({error: err}))
      return
    }
    res.statusCode = 200
    res.end(JSON.stringify({
      pr: pr
    }, true, 2))
  }

  function collabStatus (res, err, collab) {
    if (err) {
      console.log(err)
      res.statusCode = 500
      res.end(JSON.stringify({error: err}))
      return
    }
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      collab: collab
    }, true, 2))
  }

  // does this ever get called?
  // function mergedPr (res, err) {
  //   if (err) {
  //     console.log(err)
  //     res.statusCode = 500
  //     res.end(JSON.stringify({ error: err }))
  //     return
  //   }
  //   res.statusCode = 200
  //   res.setHeader('content-type', 'application/json')
  //   res.end(JSON.stringify({
  //     merged: true
  //   }, true, 2))
  // }

  return server
}
<|endoftext|>


<commit_before>'use strict';
const nbind = require('nbind');
const async_hooks = require('@mischnic/async-hooks');

const binding = nbind.init(__dirname);
let asyncHook = null;
module.exports = binding.lib;

binding.lib.Ui.init();

var setTimeoutNode;
var clearTimeoutNode;
var setIntervalNode;
var clearIntervalNode;

function stopLoop() {
	binding.lib.EventLoop.stop();

	if (!setTimeoutNode) {
		return;
	}

	asyncHook.disable();

	global.setTimeout = setTimeoutNode;
	global.clearTimeout = clearTimeoutNode;
	global.setInterval = setIntervalNode;
	global.clearInterval = clearIntervalNode;

	setTimeoutNode = null;
	clearTimeoutNode = null;
	setIntervalNode = null;
	clearIntervalNode = null;
}

function startLoop() {
	binding.lib.EventLoop.start();

	if (setTimeoutNode) {
		return;
	}

	// Create a new AsyncHook instance.

	asyncHook = async_hooks.createHook({init});

	// Allow callbacks of this AsyncHook instance to call. This is not an implicit
	// action after running the constructor, and must be explicitly run to begin
	// executing callbacks.
	asyncHook.enable();

	setTimeoutNode = global.setTimeout;

	global.setTimeout = function(cb, t) {
		const args = Array.prototype.slice.call(arguments, 2);
		return binding.lib.setTimeout(function() {
			cb.apply(null, args);
		}, t);
	};

	clearTimeoutNode = global.clearTimeout;
	global.clearTimeout = binding.lib.clearTimeout;

	setIntervalNode = global.setInterval;
	global.setInterval = function(cb, t) {
		const args = Array.prototype.slice.call(arguments, 2);
		return binding.lib.setInterval(function() {
			cb.apply(null, args);
		}, t);
	};

	clearIntervalNode = global.clearInterval;
	global.clearInterval = binding.lib.clearInterval;
}

// This is called when a new async handle
// is created. It's used to signal the background
// thread to stop awaiting calls and upgrade it's list of handles
// it's awaiting for.
let wakingup = false
function init(asyncId, type, triggerAsyncId, resource) {
	if (wakingup) {
		return;
	}
	wakingup = true;
	setImmediate(() => {
		binding.lib.EventLoop.wakeupBackgroundThread();
		wakingup = false;
	});
}

function Color(r, g, b, a) {
	this.r = r;
	this.g = g;
	this.b = b;
	this.a = a;
}

function fromJSColor(output) {
	output(this.r, this.g, this.b, this.a);
}
Color.prototype.fromJS = fromJSColor;

binding.bind('Color', Color);

function Point(x, y) {
	this.x = x;
	this.y = y;
}

function fromJSPoint(output) {
	output(this.x, this.y);
}
Point.prototype.fromJS = fromJSPoint;

binding.bind('Point', Point);

function PointDouble(x, y) {
	this.x = x;
	this.y = y;
}

function fromJSPointDouble(output) {
	output(this.x, this.y);
}

PointDouble.prototype.fromJS = fromJSPointDouble;

binding.bind('PointDouble', PointDouble);

function Size(w, h) {
	this.w = w;
	this.h = h;
}

function fromJSSize(output) {
	output(this.w, this.h);
}
Size.prototype.fromJS = fromJSSize;

binding.bind('Size', Size);

function SizeDouble(w, h) {
	this.w = w;
	this.h = h;
}

function fromJSSizeDouble(output) {
	output(this.w, this.h);
}
SizeDouble.prototype.fromJS = fromJSSizeDouble;

binding.bind('SizeDouble', SizeDouble);

function BrushGradientStop(pos, color) {
	this.pos = pos;
	this.color = color;
}

function fromJSBrushGradientStop(output) {
	output(this.pos, this.color);
}
BrushGradientStop.prototype.fromJS = fromJSBrushGradientStop;

binding.bind('BrushGradientStop', BrushGradientStop);

const textWeight = {
	minimum: 0,
	thin: 100,
	ultraLight: 200,
	light: 300,
	book: 350,
	normal: 400,
	medium: 500,
	semiBold: 600,
	bold: 700,
	ultraBold: 800,
	heavy: 900,
	ultraHeavy: 950,
	maximum: 1000
};

const textItalic = {
	normal: 0,
	oblique: 1,
	italic: 2
};

const textStretch = {
	ultraCondensed: 0,
	extraCondensed: 1,
	condensed: 2,
	semiCondensed: 3,
	normal: 4,
	semiExpanded: 5,
	expanded: 6,
	extraExpanded: 7,
	ultraExpanded: 8
};

const textAttributeType = {
	family: 0,
	size: 1,
	weight: 2,
	italic: 3,
	stretch: 4,
	color: 5,
	background: 6,
	underline: 7,
	underlineColor: 8,
	features: 9
};

const textUnderline = {
	none: 0,
	single: 1,
	double: 2,
	suggestion: 3
};

const textUnderlineColor = {
	custom: 0,
	spelling: 1,
	grammar: 2,
	auxiliary: 3
};

const textAlign = {
	left: 0,
	center: 1,
	right: 2
};

const forEach = {
	continue: 0,
	stop: 1
};

binding.lib.AttributedString.prototype.appendAttributed = function(str) {
	return this.appendAttributedInternal(str, Array.prototype.slice.call(arguments, 1));
};

binding.lib.AttributedString.prototype.insertAttributed = function(str, start) {
	return this.insertAttributedInternal(str, start, Array.prototype.slice.call(arguments, 2));
};

binding.lib.FontAttribute.newUnderlineColor = function(type, color) {
	if (type === textUnderlineColor.custom && !color) {
		console.error('With textUnderlineColor.custom, a color needs to passed');
	}
	color = color || new Color(0, 0, 0, 0);
	return binding.lib.FontAttribute.newUnderlineColor2(type, color);
};

binding.lib.OpenTypeFeatures.prototype.get = function(str) {
	const value = this.getInternal(str);
	if (value[1]) {
		return value[0];
	}
	return null;
};

binding.lib.FontAttribute.prototype.getFamily = function() {
	if (this.getAttributeType() !== textAttributeType.family) {
		return null;
	}
	return this.getFamilyInternal();
};

binding.lib.FontAttribute.prototype.getSize = function() {
	if (this.getAttributeType() !== textAttributeType.size) {
		return null;
	}
	return this.getSizeInternal();
};

binding.lib.FontAttribute.prototype.getWeight = function() {
	if (this.getAttributeType() !== textAttributeType.weight) {
		return null;
	}
	return this.getWeightInternal();
};

binding.lib.FontAttribute.prototype.getItalic = function() {
	if (this.getAttributeType() !== textAttributeType.italic) {
		return null;
	}
	return this.getItalicInternal();
};

binding.lib.FontAttribute.prototype.getStretch = function() {
	if (this.getAttributeType() !== textAttributeType.stretch) {
		return null;
	}
	return this.getStretchInternal();
};

binding.lib.FontAttribute.prototype.getColor = function() {
	if (this.getAttributeType() !== textAttributeType.color) {
		return null;
	}
	return this.getColorInternal();
};

binding.lib.FontAttribute.prototype.getUnderline = function() {
	if (this.getAttributeType() !== textAttributeType.underline) {
		return null;
	}
	return this.getUnderlineInternal();
};

binding.lib.FontAttribute.prototype.getUnderlineColor = function() {
	if (this.getAttributeType() !== textAttributeType.underlineColor) {
		return null;
	}
	const v = this.getUnderlineColorInternal();
	const type = Math.round(v[1].r);
	return {type, color: type === textUnderlineColor.custom ? v[0] : null};
};

binding.lib.FontAttribute.prototype.getOTFeatures = function() {
	if (this.getAttributeType() !== textAttributeType.features) {
		return null;
	}
	return this.getOTFeaturesInternal();
};

const brushType = {
	solid: 0,
	linearGradient: 1,
	radialGradient: 2
	// IMPL uiDrawBrushTypeImage: 3
};

const lineCap = {
	flat: 0,
	round: 1,
	square: 2
};

const lineJoin = {
	miter: 0,
	round: 1,
	bevel: 2
};

const fillMode = {
	winding: 0,
	alternate: 1
};

const modifierKeys = {
	ctrl: 1 << 0,
	alt: 1 << 1,
	shift: 1 << 2,
	super: 1 << 3
};

const extKeys = {
	escape: 1,
	insert: 2, // equivalent to "Help" on Apple keyboards
	delete: 3,
	home: 4,
	end: 5,
	pageUp: 6,
	pageDown: 7,
	up: 8,
	down: 9,
	left: 10,
	right: 11,
	f1: 12, // F1..F12 are guaranteed to be consecutive
	f2: 13,
	f3: 14,
	f4: 15,
	f5: 16,
	f6: 17,
	f7: 18,
	f8: 19,
	f9: 20,
	f10: 21,
	f11: 22,
	f12: 23,
	n0: 24, // numpad keys; independent of Num Lock state
	n1: 25, // N0..N9 are guaranteed to be consecutive
	n2: 26,
	n3: 27,
	n4: 28,
	n5: 29,
	n6: 30,
	n7: 31,
	n8: 32,
	n9: 33,
	nDot: 34,
	nEnter: 35,
	nAdd: 36,
	nSubtract: 37,
	nMultiply: 38,
	nDivide: 39
};

module.exports.textStretch = textStretch;
module.exports.textItalic = textItalic;
module.exports.textWeight = textWeight;
module.exports.textItalic = textItalic;
module.exports.textStretch = textStretch;
module.exports.textAttributeType = textAttributeType;
module.exports.textUnderline = textUnderline;
module.exports.textUnderlineColor = textUnderlineColor;
module.exports.textAlign = textAlign;
module.exports.forEach = forEach;
module.exports.Size = Size;
module.exports.Point = Point;
module.exports.Color = Color;
module.exports.PointDouble = PointDouble;
module.exports.SizeDouble = SizeDouble;
module.exports.BrushGradientStop = BrushGradientStop;
module.exports.brushType = brushType;
module.exports.lineCap = lineCap;
module.exports.lineJoin = lineJoin;
module.exports.fillMode = fillMode;
module.exports.modifierKeys = modifierKeys;
module.exports.extKeys = extKeys;
module.exports.startLoop = startLoop;
module.exports.stopLoop = stopLoop;
module.exports.onShouldQuit = binding.lib.Ui.onShouldQuit;
<commit_msg>Handle null with clearTimeout/Interval<commit_after>'use strict';
const nbind = require('nbind');
const async_hooks = require('@mischnic/async-hooks');

const binding = nbind.init(__dirname);
let asyncHook = null;
module.exports = binding.lib;

binding.lib.Ui.init();

var setTimeoutNode;
var clearTimeoutNode;
var setIntervalNode;
var clearIntervalNode;

function stopLoop() {
	binding.lib.EventLoop.stop();

	if (!setTimeoutNode) {
		return;
	}

	asyncHook.disable();

	global.setTimeout = setTimeoutNode;
	global.clearTimeout = clearTimeoutNode;
	global.setInterval = setIntervalNode;
	global.clearInterval = clearIntervalNode;

	setTimeoutNode = null;
	clearTimeoutNode = null;
	setIntervalNode = null;
	clearIntervalNode = null;
}

function startLoop() {
	binding.lib.EventLoop.start();

	if (setTimeoutNode) {
		return;
	}

	// Create a new AsyncHook instance.

	asyncHook = async_hooks.createHook({init});

	// Allow callbacks of this AsyncHook instance to call. This is not an implicit
	// action after running the constructor, and must be explicitly run to begin
	// executing callbacks.
	asyncHook.enable();

	setTimeoutNode = global.setTimeout;
	global.setTimeout = function(cb, t) {
		const args = Array.prototype.slice.call(arguments, 2);
		return binding.lib.setTimeout(function() {
			cb.apply(null, args);
		}, t);
	};

	clearTimeoutNode = global.clearTimeout;
	global.clearTimeout = function(obj) {
		if (obj) {
			binding.lib.clearTimeout(obj);
		}
	};

	setIntervalNode = global.setInterval;
	global.setInterval = function(cb, t) {
		const args = Array.prototype.slice.call(arguments, 2);
		return binding.lib.setInterval(function() {
			cb.apply(null, args);
		}, t);
	};

	clearIntervalNode = global.clearInterval;
	global.clearInterval = function(obj) {
		if (obj) {
			binding.lib.clearInterval(obj);
		}
	}
}

// This is called when a new async handle
// is created. It's used to signal the background
// thread to stop awaiting calls and upgrade it's list of handles
// it's awaiting for.
let wakingup = false
function init(asyncId, type, triggerAsyncId, resource) {
	if (wakingup) {
		return;
	}
	wakingup = true;
	setImmediate(() => {
		binding.lib.EventLoop.wakeupBackgroundThread();
		wakingup = false;
	});
}

function Color(r, g, b, a) {
	this.r = r;
	this.g = g;
	this.b = b;
	this.a = a;
}

function fromJSColor(output) {
	output(this.r, this.g, this.b, this.a);
}
Color.prototype.fromJS = fromJSColor;

binding.bind('Color', Color);

function Point(x, y) {
	this.x = x;
	this.y = y;
}

function fromJSPoint(output) {
	output(this.x, this.y);
}
Point.prototype.fromJS = fromJSPoint;

binding.bind('Point', Point);

function PointDouble(x, y) {
	this.x = x;
	this.y = y;
}

function fromJSPointDouble(output) {
	output(this.x, this.y);
}

PointDouble.prototype.fromJS = fromJSPointDouble;

binding.bind('PointDouble', PointDouble);

function Size(w, h) {
	this.w = w;
	this.h = h;
}

function fromJSSize(output) {
	output(this.w, this.h);
}
Size.prototype.fromJS = fromJSSize;

binding.bind('Size', Size);

function SizeDouble(w, h) {
	this.w = w;
	this.h = h;
}

function fromJSSizeDouble(output) {
	output(this.w, this.h);
}
SizeDouble.prototype.fromJS = fromJSSizeDouble;

binding.bind('SizeDouble', SizeDouble);

function BrushGradientStop(pos, color) {
	this.pos = pos;
	this.color = color;
}

function fromJSBrushGradientStop(output) {
	output(this.pos, this.color);
}
BrushGradientStop.prototype.fromJS = fromJSBrushGradientStop;

binding.bind('BrushGradientStop', BrushGradientStop);

const textWeight = {
	minimum: 0,
	thin: 100,
	ultraLight: 200,
	light: 300,
	book: 350,
	normal: 400,
	medium: 500,
	semiBold: 600,
	bold: 700,
	ultraBold: 800,
	heavy: 900,
	ultraHeavy: 950,
	maximum: 1000
};

const textItalic = {
	normal: 0,
	oblique: 1,
	italic: 2
};

const textStretch = {
	ultraCondensed: 0,
	extraCondensed: 1,
	condensed: 2,
	semiCondensed: 3,
	normal: 4,
	semiExpanded: 5,
	expanded: 6,
	extraExpanded: 7,
	ultraExpanded: 8
};

const textAttributeType = {
	family: 0,
	size: 1,
	weight: 2,
	italic: 3,
	stretch: 4,
	color: 5,
	background: 6,
	underline: 7,
	underlineColor: 8,
	features: 9
};

const textUnderline = {
	none: 0,
	single: 1,
	double: 2,
	suggestion: 3
};

const textUnderlineColor = {
	custom: 0,
	spelling: 1,
	grammar: 2,
	auxiliary: 3
};

const textAlign = {
	left: 0,
	center: 1,
	right: 2
};

const forEach = {
	continue: 0,
	stop: 1
};

binding.lib.AttributedString.prototype.appendAttributed = function(str) {
	return this.appendAttributedInternal(str, Array.prototype.slice.call(arguments, 1));
};

binding.lib.AttributedString.prototype.insertAttributed = function(str, start) {
	return this.insertAttributedInternal(str, start, Array.prototype.slice.call(arguments, 2));
};

binding.lib.FontAttribute.newUnderlineColor = function(type, color) {
	if (type === textUnderlineColor.custom && !color) {
		console.error('With textUnderlineColor.custom, a color needs to passed');
	}
	color = color || new Color(0, 0, 0, 0);
	return binding.lib.FontAttribute.newUnderlineColor2(type, color);
};

binding.lib.OpenTypeFeatures.prototype.get = function(str) {
	const value = this.getInternal(str);
	if (value[1]) {
		return value[0];
	}
	return null;
};

binding.lib.FontAttribute.prototype.getFamily = function() {
	if (this.getAttributeType() !== textAttributeType.family) {
		return null;
	}
	return this.getFamilyInternal();
};

binding.lib.FontAttribute.prototype.getSize = function() {
	if (this.getAttributeType() !== textAttributeType.size) {
		return null;
	}
	return this.getSizeInternal();
};

binding.lib.FontAttribute.prototype.getWeight = function() {
	if (this.getAttributeType() !== textAttributeType.weight) {
		return null;
	}
	return this.getWeightInternal();
};

binding.lib.FontAttribute.prototype.getItalic = function() {
	if (this.getAttributeType() !== textAttributeType.italic) {
		return null;
	}
	return this.getItalicInternal();
};

binding.lib.FontAttribute.prototype.getStretch = function() {
	if (this.getAttributeType() !== textAttributeType.stretch) {
		return null;
	}
	return this.getStretchInternal();
};

binding.lib.FontAttribute.prototype.getColor = function() {
	if (this.getAttributeType() !== textAttributeType.color) {
		return null;
	}
	return this.getColorInternal();
};

binding.lib.FontAttribute.prototype.getUnderline = function() {
	if (this.getAttributeType() !== textAttributeType.underline) {
		return null;
	}
	return this.getUnderlineInternal();
};

binding.lib.FontAttribute.prototype.getUnderlineColor = function() {
	if (this.getAttributeType() !== textAttributeType.underlineColor) {
		return null;
	}
	const v = this.getUnderlineColorInternal();
	const type = Math.round(v[1].r);
	return {type, color: type === textUnderlineColor.custom ? v[0] : null};
};

binding.lib.FontAttribute.prototype.getOTFeatures = function() {
	if (this.getAttributeType() !== textAttributeType.features) {
		return null;
	}
	return this.getOTFeaturesInternal();
};

const brushType = {
	solid: 0,
	linearGradient: 1,
	radialGradient: 2
	// IMPL uiDrawBrushTypeImage: 3
};

const lineCap = {
	flat: 0,
	round: 1,
	square: 2
};

const lineJoin = {
	miter: 0,
	round: 1,
	bevel: 2
};

const fillMode = {
	winding: 0,
	alternate: 1
};

const modifierKeys = {
	ctrl: 1 << 0,
	alt: 1 << 1,
	shift: 1 << 2,
	super: 1 << 3
};

const extKeys = {
	escape: 1,
	insert: 2, // equivalent to "Help" on Apple keyboards
	delete: 3,
	home: 4,
	end: 5,
	pageUp: 6,
	pageDown: 7,
	up: 8,
	down: 9,
	left: 10,
	right: 11,
	f1: 12, // F1..F12 are guaranteed to be consecutive
	f2: 13,
	f3: 14,
	f4: 15,
	f5: 16,
	f6: 17,
	f7: 18,
	f8: 19,
	f9: 20,
	f10: 21,
	f11: 22,
	f12: 23,
	n0: 24, // numpad keys; independent of Num Lock state
	n1: 25, // N0..N9 are guaranteed to be consecutive
	n2: 26,
	n3: 27,
	n4: 28,
	n5: 29,
	n6: 30,
	n7: 31,
	n8: 32,
	n9: 33,
	nDot: 34,
	nEnter: 35,
	nAdd: 36,
	nSubtract: 37,
	nMultiply: 38,
	nDivide: 39
};

module.exports.textStretch = textStretch;
module.exports.textItalic = textItalic;
module.exports.textWeight = textWeight;
module.exports.textItalic = textItalic;
module.exports.textStretch = textStretch;
module.exports.textAttributeType = textAttributeType;
module.exports.textUnderline = textUnderline;
module.exports.textUnderlineColor = textUnderlineColor;
module.exports.textAlign = textAlign;
module.exports.forEach = forEach;
module.exports.Size = Size;
module.exports.Point = Point;
module.exports.Color = Color;
module.exports.PointDouble = PointDouble;
module.exports.SizeDouble = SizeDouble;
module.exports.BrushGradientStop = BrushGradientStop;
module.exports.brushType = brushType;
module.exports.lineCap = lineCap;
module.exports.lineJoin = lineJoin;
module.exports.fillMode = fillMode;
module.exports.modifierKeys = modifierKeys;
module.exports.extKeys = extKeys;
module.exports.startLoop = startLoop;
module.exports.stopLoop = stopLoop;
module.exports.onShouldQuit = binding.lib.Ui.onShouldQuit;
<|endoftext|>