<commit_before>/**

  Import a data file into MongoDB

**/

var fs = require("fs");
var async = require("async");
var _ = require("underscore");
var Utils = require("../utils");
var debug = require("debug")("Importer");
var debugWarning = require("debug")("Importer:Warning");

var KEEP_REGEX = new RegExp("\{keep\}", "i");
var FILTER_REGEX = /\{\s*\^(\w+)\(([\w<>,\s]*)\)\s*\}/i;

module.exports = function (factSystem, Topic, Gambit, Reply) {

  var _rawToGambitData = function (gambitId, itemData) {
    var gambitData = {
      id: gambitId,
      isQuestion: itemData.options.isQuestion,
      qType: itemData.options.qType === false ? "" : itemData.options.qType,
      qSubType: itemData.options.qSubType === false ? "" : itemData.options.qSubType,
      filter: itemData.options.filter === false ? "" : itemData.options.filter,
      trigger: itemData.trigger
    };

    // This is to capture anything pre 5.1
    if (itemData.raw) {
      gambitData.input = itemData.raw;
    } else {
      gambitData.input = itemData.trigger;
    }

    if (itemData.redirect !== null) {
      gambitData.redirect = itemData.redirect;
    }

    return gambitData;
  };

  var importData = function (data, callback, flushTopics, preserveRandom) {

    var gambitsWithConversation = [];

    var eachReplyItor = function (gambit) {
      return function (replyId, nextReply) {
        debug("Reply process: " + replyId);
        var replyString = data.replys[replyId];
        var properties = { id: replyId, reply: replyString};
        var match = replyString.match(KEEP_REGEX);
        if (match) {
          properties.keep = true;
          properties.reply = Utils.trim(replyString.replace(match[0], ""));
        }
        match = replyString.match(FILTER_REGEX);
        if (match) {
          properties.filter = "^" + match[1] + "(" + match[2] + ")";
          properties.reply = Utils.trim(replyString.replace(match[0], ""));
        }
        gambit.addReply(properties, function (err) {
          if (err) {
            console.log(err);
          }

          debug("Reply processed.");
          nextReply();
        });
      };
    };

    var findOrCreateTopic = function (topicName, properties, callback) {
      // if (topicName === 'pre' || topicName === 'post') topicName = '__' + topicName + '__';
      Topic.findOrCreate({name: topicName}, properties, function (err, topic) {
        if (flushTopics && !(topicName === 'random' && preserveRandom)) {
          topic.clearGambits(function() {
            Topic.remove({ _id: topic.id }, function (err2) {
              if (err2) {
                console.log(err);
              }

              debug('removed topic ' + topicName + ' (' + topic.id + ')');

              Topic.findOrCreate({name: topicName}, properties, function (err3, topic2) {
                callback(err3, topic2);
              });
            });
          });
        } else {
          callback(err, topic);
        }
      });
    };

    var eachTopicItor = function (topicName, nextTopic) {
      debug("Find or create", topicName);
      var properties = {
        name: topicName,
        keep: data.topics[topicName].flags.indexOf("keep") !== -1,
        system: data.topics[topicName].flags.indexOf("system") !== -1,
        keywords: data.topics[topicName].keywords ? data.topics[topicName].keywords : []
      };

      findOrCreateTopic(topicName, properties, function (err, topic) {
        if (err) {
          console.log(err);
        }

        debug("topic processed.");

        var eachGambitItor = function (gambitId, nextGambit) {
          if (!_.isUndefined(data.gambits[gambitId].options.conversations)) {

            gambitsWithConversation.push(gambitId);
            nextGambit();

          } else if (data.gambits[gambitId].topic === topicName) {
            debug("Gambit process: " + gambitId);
            var gambitRawData = data.gambits[gambitId];
            var gambitData = _rawToGambitData(gambitId, gambitRawData);

            topic.createGambit(gambitData, function (err3, gambit) {
              if (err3) {
                return new Error(err3);
              }
              async.eachSeries(gambitRawData.replys, eachReplyItor(gambit), function (err4) {
                if (err4) {
                  return new Error(err4);
                }
                debug("All replys processed.");
                nextGambit();
              });
            });
          } else {
            nextGambit();
          }
        };

        async.eachSeries(Object.keys(data.gambits), eachGambitItor, function (err5) {
          if (err5) {
            console.log(err5);
          }
          debug("All gambits for " + topicName + " processed.");
          nextTopic();
        });
      });
    };

    var eachConvItor = function (gambitId) {
      return function (replyId, nextConv) {
        debug("conversation/reply: " + replyId);
        Reply.findOne({id: replyId}, function (err, reply) {
          if (err) {
            console.log(err);
          }
          if (reply) {
            reply.gambits.addToSet(gambitId);
            reply.save(function () {
              reply.sortGambits(function () {
                debug("All conversations for " + gambitId + " processed.");
                nextConv();
              });
            });
          } else {
            debugWarning("No reply found!");
            nextConv();
          }
        });
      };
    };

    async.eachSeries(Object.keys(data.topics), eachTopicItor, function () {

      async.eachSeries(_.uniq(gambitsWithConversation), function (gambitId, finish) {
        var gambitData = data.gambits[gambitId];
        var conversations = gambitData.options.conversations || [];
        if (conversations.length === 0) {
          return finish();
        }

        var convoGambit = _rawToGambitData(gambitId, gambitData);
        var cGambit = new Gambit(convoGambit);

        async.eachSeries(gambitData.replys, eachReplyItor(cGambit), function () {
          debug("All replys processed.");
          cGambit.save(function (err, gam) {
            debug("Saving New Gambit", err, gam);
            async.map(conversations, eachConvItor(gam._id), function () {
              debug("All conversations for " + gambitId + " processed.");
              finish();
            });
          });
        });
      }, function () {
        callback(null, "done");
      });
    });
  };

  var importFile = function (path, callback) {

      var fs = require('fs');

      fs.readFile(path, function(err, jsonFile){
          //var data = JSON.parse(jsonFile);
          return importData(JSON.parse(jsonFile), callback);
      });
  };

  return {
    file: importFile,
    data: importData
  };
};
<commit_msg>no message<commit_after>/**

  Import a data file into MongoDB

**/

var fs = require("fs");
var async = require("async");
var _ = require("underscore");
var Utils = require("../utils");
var debug = require("debug")("Importer");
var debugWarning = require("debug")("Importer:Warning");

var KEEP_REGEX = new RegExp("\{keep\}", "i");
var FILTER_REGEX = /\{\s*\^(\w+)\(([\w<>,\s]*)\)\s*\}/i;

module.exports = function (factSystem, Topic, Gambit, Reply) {

  var _rawToGambitData = function (gambitId, itemData) {
    var gambitData = {
      id: gambitId,
      isQuestion: itemData.options.isQuestion,
      qType: itemData.options.qType === false ? "" : itemData.options.qType,
      qSubType: itemData.options.qSubType === false ? "" : itemData.options.qSubType,
      filter: itemData.options.filter === false ? "" : itemData.options.filter,
      trigger: itemData.trigger
    };

    // This is to capture anything pre 5.1
    if (itemData.raw) {
      gambitData.input = itemData.raw;
    } else {
      gambitData.input = itemData.trigger;
    }

    if (itemData.redirect !== null) {
      gambitData.redirect = itemData.redirect;
    }

    return gambitData;
  };

  var importData = function (data, callback, flushTopics, preserveRandom) {

    var gambitsWithConversation = [];

    var eachReplyItor = function (gambit) {
      return function (replyId, nextReply) {
        debug("Reply process: " + replyId);
        var replyString = data.replys[replyId];
        var properties = { id: replyId, reply: replyString};
        var match = replyString.match(KEEP_REGEX);
        if (match) {
          properties.keep = true;
          properties.reply = Utils.trim(replyString.replace(match[0], ""));
        }
        match = replyString.match(FILTER_REGEX);
        if (match) {
          properties.filter = "^" + match[1] + "(" + match[2] + ")";
          properties.reply = Utils.trim(replyString.replace(match[0], ""));
        }
        gambit.addReply(properties, function (err) {
          if (err) {
            console.log(err);
          }

          debug("Reply processed.");
          nextReply();
        });
      };
    };

    var findOrCreateTopic = function (topicName, properties, callback) {
      Topic.findOrCreate({name: topicName}, properties, function (err, topic) {
        if (flushTopics && !(topicName === 'random' && preserveRandom)) {
          topic.clearGambits(function() {
            Topic.remove({ _id: topic.id }, function (err2) {
              if (err2) {
                console.log(err);
              }

              debug('removed topic ' + topicName + ' (' + topic.id + ')');

              Topic.findOrCreate({name: topicName}, properties, function (err3, topic2) {
                callback(err3, topic2);
              });
            });
          });
        } else {
          callback(err, topic);
        }
      });
    };

    var eachTopicItor = function (topicName, nextTopic) {
      debug("Find or create", topicName);
      var properties = {
        name: topicName,
        keep: data.topics[topicName].flags.indexOf("keep") !== -1,
        system: data.topics[topicName].flags.indexOf("system") !== -1,
        keywords: data.topics[topicName].keywords ? data.topics[topicName].keywords : []
      };

      findOrCreateTopic(topicName, properties, function (err, topic) {
        if (err) {
          console.log(err);
        }

        debug("topic processed.");

        var eachGambitItor = function (gambitId, nextGambit) {
          if (!_.isUndefined(data.gambits[gambitId].options.conversations)) {

            gambitsWithConversation.push(gambitId);
            nextGambit();

          } else if (data.gambits[gambitId].topic === topicName) {
            debug("Gambit process: " + gambitId);
            var gambitRawData = data.gambits[gambitId];
            var gambitData = _rawToGambitData(gambitId, gambitRawData);

            topic.createGambit(gambitData, function (err3, gambit) {
              if (err3) {
                return new Error(err3);
              }
              async.eachSeries(gambitRawData.replys, eachReplyItor(gambit), function (err4) {
                if (err4) {
                  return new Error(err4);
                }
                debug("All replys processed.");
                nextGambit();
              });
            });
          } else {
            nextGambit();
          }
        };

        async.eachSeries(Object.keys(data.gambits), eachGambitItor, function (err5) {
          if (err5) {
            console.log(err5);
          }
          debug("All gambits for " + topicName + " processed.");
          nextTopic();
        });
      });
    };

    var eachConvItor = function (gambitId) {
      return function (replyId, nextConv) {
        debug("conversation/reply: " + replyId);
        Reply.findOne({id: replyId}, function (err, reply) {
          if (err) {
            console.log(err);
          }
          if (reply) {
            reply.gambits.addToSet(gambitId);
            reply.save(function () {
              reply.sortGambits(function () {
                debug("All conversations for " + gambitId + " processed.");
                nextConv();
              });
            });
          } else {
            debugWarning("No reply found!");
            nextConv();
          }
        });
      };
    };

    async.eachSeries(Object.keys(data.topics), eachTopicItor, function () {

      async.eachSeries(_.uniq(gambitsWithConversation), function (gambitId, finish) {
        var gambitData = data.gambits[gambitId];
        var conversations = gambitData.options.conversations || [];
        if (conversations.length === 0) {
          return finish();
        }

        var convoGambit = _rawToGambitData(gambitId, gambitData);
        var cGambit = new Gambit(convoGambit);

        async.eachSeries(gambitData.replys, eachReplyItor(cGambit), function () {
          debug("All replys processed.");
          cGambit.save(function (err, gam) {
            debug("Saving New Gambit", err, gam);
            async.map(conversations, eachConvItor(gam._id), function () {
              debug("All conversations for " + gambitId + " processed.");
              finish();
            });
          });
        });
      }, function () {
        callback(null, "done");
      });
    });
  };

  var importFile = function (path, callback) {

      var fs = require('fs');

      fs.readFile(path, function(err, jsonFile){
          //var data = JSON.parse(jsonFile);
          return importData(JSON.parse(jsonFile), callback);
      });
  };

  return {
    file: importFile,
    data: importData
  };
};
<|endoftext|>


<commit_before>// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
require('../../js/transition.js')
require('../../js/alert.js')
require('../../js/button.js')
require('../../js/carousel.js')
require('../../js/collapse.js')
require('../../js/dropdown.js')
require('../../js/modal.js')
require('../../js/tooltip.js')
require('../../js/popover.js')
require('../../js/scrollspy.js')
require('../../js/tab.js')
require('../../js/affix.js')<commit_msg>Delete npm.js<commit_after><|endoftext|>


<commit_before>import React from 'react';
import Enzyme, {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';
import Cell from '../src/Cell';

Enzyme.configure({adapter: new Adapter()});

it('renders hidden when hidden', () => {
  const cell = mount(<Cell isHidden={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-hidden')).toHaveLength(1);
});

it('renders black when black', () => {
  const cell = mount(<Cell isBlack={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-black')).toHaveLength(1);
});

it('renders shaded when shaded', () => {
  const cell = mount(<Cell isShaded={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-shaded')).toHaveLength(1);
});

it('renders left border when left', () => {
  const cell = mount(<Cell isLeft={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-left')).toHaveLength(1);
});

it('renders top border when top', () => {
  const cell = mount(<Cell isTop={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-top')).toHaveLength(1);
});

it('renders in focus when focused', () => {
  const cell = mount(<Cell isFocus={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-focus')).toHaveLength(1);
});

it('renders in incorrect focus when focused and incorrect', () => {
  const cell = mount(<Cell isFocus={true} isIncorrect={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-focus-incorrect')).toHaveLength(1);
});

it('renders active when active', () => {
  const cell = mount(<Cell isActive={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-active')).toHaveLength(1);
});

it('renders active and incorrect when active and incorrect', () => {
  const cell = mount(<Cell isActive={true} isIncorrect={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-active-incorrect')).toHaveLength(1);
});

it('renders incorrect when incorrect and not hidden', () => {
  const cell = mount(<Cell isIncorrect={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-incorrect')).toHaveLength(1);

  const cell2 = mount(<Cell isIncorrect={true} isHidden={true} value=" "/>);
  expect(cell2.find('div.xwordjs-cell-incorrect')).toHaveLength(0);
});

it('renders circled when circled', () => {
  const cell = mount(<Cell isCircled={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-circled')).toHaveLength(1);
});

it('renders smaller font when rebus', () => {
  function fontSize(cell) {
    return parseInt(getComputedStyle(cell
      .find('div.xwordjs-cell-text').getDOMNode())
      .getPropertyValue("font-size"));
  }

  const normalCell = mount(<Cell value="A"/>);
  const rebusCell = mount(<Cell value="ABCDEF"/>);

  const normalSize = fontSize(normalCell);
  const rebusSize = fontSize(rebusCell);

  console.debug("normal " + normalSize);
  console.debug("rebus " + rebusSize);

  expect(rebusSize).toBeLessThan(normalSize);
});
<commit_msg>remove debugging code<commit_after>import React from 'react';
import Enzyme, {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';
import Cell from '../src/Cell';

Enzyme.configure({adapter: new Adapter()});

it('renders hidden when hidden', () => {
  const cell = mount(<Cell isHidden={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-hidden')).toHaveLength(1);
});

it('renders black when black', () => {
  const cell = mount(<Cell isBlack={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-black')).toHaveLength(1);
});

it('renders shaded when shaded', () => {
  const cell = mount(<Cell isShaded={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-shaded')).toHaveLength(1);
});

it('renders left border when left', () => {
  const cell = mount(<Cell isLeft={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-left')).toHaveLength(1);
});

it('renders top border when top', () => {
  const cell = mount(<Cell isTop={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-top')).toHaveLength(1);
});

it('renders in focus when focused', () => {
  const cell = mount(<Cell isFocus={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-focus')).toHaveLength(1);
});

it('renders in incorrect focus when focused and incorrect', () => {
  const cell = mount(<Cell isFocus={true} isIncorrect={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-focus-incorrect')).toHaveLength(1);
});

it('renders active when active', () => {
  const cell = mount(<Cell isActive={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-active')).toHaveLength(1);
});

it('renders active and incorrect when active and incorrect', () => {
  const cell = mount(<Cell isActive={true} isIncorrect={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-active-incorrect')).toHaveLength(1);
});

it('renders incorrect when incorrect and not hidden', () => {
  const cell = mount(<Cell isIncorrect={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-incorrect')).toHaveLength(1);

  const cell2 = mount(<Cell isIncorrect={true} isHidden={true} value=" "/>);
  expect(cell2.find('div.xwordjs-cell-incorrect')).toHaveLength(0);
});

it('renders circled when circled', () => {
  const cell = mount(<Cell isCircled={true} value=" "/>);
  expect(cell.find('div.xwordjs-cell-circled')).toHaveLength(1);
});

it('renders smaller font when rebus', () => {
  function fontSize(cell) {
    return parseInt(getComputedStyle(cell
      .find('div.xwordjs-cell-text').getDOMNode())
      .getPropertyValue("font-size"));
  }

  const normalCell = mount(<Cell value="A"/>);
  const rebusCell = mount(<Cell value="ABCDEF"/>);

  const normalSize = fontSize(normalCell);
  const rebusSize = fontSize(rebusCell);

  expect(rebusSize).toBeLessThan(normalSize);
});
<|endoftext|>


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

var url = require('url');
var querystring = require('querystring');

var logmagic = require('logmagic');
var request = require('request');

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

/**
 * BuildBot Client.
 * @constructor
 * @param {Object} options The options.
 */
function BuildBot(options, log) {
  var parsed = url.parse(options.url);

  // Inject auth into the URL
  delete parsed.host;
  parsed['auth'] = sprintf('%s:%s', options.username, options.password);

  this._url = url.format(parsed);
  this._options = options;
  this.log = log || logmagic.local('buildbot');
}

/**
 * Initiate build on buildbot
 * @param {String} builder The builder to force the build on.
 * @param {String} revision The revision to build.
 * @param {Function} callback The completion callback(err).
 */
BuildBot.prototype.build = function(builder, revision, callback) {
  var self = this,
      reqOpts = {
        method: 'POST',
        uri: sprintf('%s/builders/%s/force', this._url, builder),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: querystring.stringify({
          username: this._options.username,
          revision: revision
        })
      };

  this.log.infof('forcing build of ${revision} on ${builder}', {
    builder: builder,
    revision: revision
  });

  request(reqOpts, function(err, response, body) {
    if (err) {
      callback(err);
    } else if (response.statusCode !== 302) {
      callback(new Error(sprintf('Received %s from %s', response.statusCode, self._options.url)));
    } else {
      callback(null, body);
    }
  });
};

/**
 * Find the oldest build with a given revision.
 */
BuildBot.prototype._findOldestBuild = function(builds, revision) {
  var i, j, number, numbers, build, properties, retrybuild;

  // Get numbers of builds from oldest to newest
  numbers = Object.keys(builds).map(function(numstr) {
    return parseInt(numstr);
  }).sort(function(a, b) {
    return a - b;
  });

  for (i = 0; i < numbers.length; i++) {
    number = numbers[i].toString();

    if (builds.hasOwnProperty(number)) {
      build = builds[number];
      properties = build.properties;

      if (!properties) {
        this.log.warnf('invalid build at ${index}: ${text}', {
          index: number,
          text: build.error
        });
        continue;
      }

      for (j = 0; j < properties.length; j++) {
        if (properties[j][0] === 'got_revision' && properties[j][1] === revision) {
          // If a build failed due to an exception, see if a subsequent one
          // can be used instead.
          if (build.results === 4) {
            retrybuild = build;
            continue;
          }

          return build;
        }
      }
    }
  }

  return retrybuild;
};

/**
 * Get a build for a specified revision.
 * @param {String} revision The revision to search for.
 * @param {Function} callback A callback fired with (err, build).
 */
BuildBot.prototype.getRevision = function(builder, revision, callback) {
  var self = this,
      selects = [],
      search, i;

  for (i = 1; i <= this._options.num_builds; i++) {
    selects.push(sprintf('-%s', i));
  }

  search = querystring.stringify({select: selects});

  request.get(sprintf('%s/json/builders/%s/builds?%s', this._url, builder, search), function(err, response, body) {
    if (err) {
      callback(err);
    } else {
      try {
        body = JSON.parse(body);
      } catch (e) {
        callback(e);
        return;
      }

      callback(null, self._findOldestBuild(body, revision));
    }
  });
};

/**
 *
 */
BuildBot.prototype.ensureRevisionBuilt = function(builder, revision, callback) {
  var self = this,
      attempts = 0;

  this.log.infof('ensuring revision ${revision} on ${builder}', {
    revision: revision,
    builder: builder
  });

  function attempt(force) {
    attempts++;

    if (attempts > self._options.retries) {
      callback(new Error(sprintf('No build found after %s attempts', self._options.retries)));
      return;
    }

    self.getRevision(builder, revision, function(err, build) {
      if (err) {
        callback(err);
      } else if (!build) {
        if (force) {
          self.build(builder, revision, function(err) {
            if (err) {
              callback(err);
            } else {
              setTimeout(attempt, self._options.delay);
            }
          });
        } else {
          // The build was already forced, but isn't showing up - it is
          // *probably* pending. Alternatively, maybe num_builds happened
          // and we missed it. Oops.
          log.infof('build of ${revision} on ${builder} not available', {
            revision: revision,
            builder: builder
          });
          setTimeout(attempt, self._options.delay);
        }
      } else if (build.times[1] !== null) {
        self.log.infof('build ${number} on ${builder} finished: ${text}', {
          number: build.number,
          result_code: build.results || 0,
          builder: builder,
          text: build.text.join(' ')
        });
        // There is a Buildbot bug where build.results isn't included when it
        // is 0
        if ((build.results || 0) === 0) {
          callback(null, build);
        } else {
          callback(new Error(sprintf('Build %s of %s: %s', build.number, builder, build.text.join(' '))));
        }
      } else {
        self.log.infof('build ${number} on ${builder} ETA is ${eta}s', {
          number: build.number,
          builder: builder,
          eta: build.eta
        });
        // Try again in the greater of half of the ETA or the configured delay
        setTimeout(attempt, Math.max(build.eta * 500, self._options.delay));
      }
    });
  }

  attempt(true);
};

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

var url = require('url');
var querystring = require('querystring');

var logmagic = require('logmagic');
var request = require('request');

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

/**
 * BuildBot Client.
 * @constructor
 * @param {Object} options The options.
 */
function BuildBot(options, log) {
  var parsed = url.parse(options.url);

  // Inject auth into the URL
  delete parsed.host;
  parsed['auth'] = sprintf('%s:%s', options.username, options.password);

  this._url = url.format(parsed);
  this._options = options;
  this.log = log || logmagic.local('buildbot');
}

/**
 * Initiate build on buildbot
 * @param {String} builder The builder to force the build on.
 * @param {String} revision The revision to build.
 * @param {Function} callback The completion callback(err).
 */
BuildBot.prototype.build = function(builder, revision, callback) {
  var self = this,
      reqOpts = {
        method: 'POST',
        uri: sprintf('%s/builders/%s/force', this._url, builder),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: querystring.stringify({
          username: this._options.username,
          revision: revision
        })
      };

  this.log.infof('forcing build of ${revision} on ${builder}', {
    builder: builder,
    revision: revision
  });

  request(reqOpts, function(err, response, body) {
    if (err) {
      callback(err);
    } else if (response.statusCode !== 302) {
      callback(new Error(sprintf('Received %s from %s', response.statusCode, self._options.url)));
    } else {
      callback(null, body);
    }
  });
};

/**
 * Find the oldest build with a given revision.
 */
BuildBot.prototype._findOldestBuild = function(builds, revision) {
  var i, j, number, numbers, build, properties, retrybuild;

  // Get numbers of builds from oldest to newest
  numbers = Object.keys(builds).map(function(numstr) {
    return parseInt(numstr);
  }).sort(function(a, b) {
    return a - b;
  });

  for (i = 0; i < numbers.length; i++) {
    number = numbers[i].toString();

    if (builds.hasOwnProperty(number)) {
      build = builds[number];
      properties = build.properties;

      if (!properties) {
        this.log.warnf('invalid build at ${index}: ${text}', {
          index: number,
          text: build.error
        });
        continue;
      }

      for (j = 0; j < properties.length; j++) {
        if (properties[j][0] === 'got_revision' && properties[j][1] === revision) {
          // If a build failed due to an exception, see if a subsequent one
          // can be used instead.
          if (build.results === 4) {
            retrybuild = build;
            continue;
          }

          return build;
        }
      }
    }
  }

  return retrybuild;
};

/**
 * Get a build for a specified revision.
 * @param {String} revision The revision to search for.
 * @param {Function} callback A callback fired with (err, build).
 */
BuildBot.prototype.getRevision = function(builder, revision, callback) {
  var self = this,
      selects = [],
      search, i;

  for (i = 1; i <= this._options.num_builds; i++) {
    selects.push(sprintf('-%s', i));
  }

  search = querystring.stringify({select: selects});

  request.get(sprintf('%s/json/builders/%s/builds?%s', this._url, builder, search), function(err, response, body) {
    if (err) {
      callback(err);
    } else {
      try {
        body = JSON.parse(body);
      } catch (e) {
        callback(e);
        return;
      }

      callback(null, self._findOldestBuild(body, revision));
    }
  });
};

/**
 *
 */
BuildBot.prototype.ensureRevisionBuilt = function(builder, revision, callback) {
  var self = this,
      attempts = 0;

  this.log.infof('ensuring revision ${revision} on ${builder}', {
    revision: revision,
    builder: builder
  });

  function attempt(force) {
    attempts++;

    if (attempts > self._options.retries) {
      callback(new Error(sprintf('No build found after %s attempts', self._options.retries)));
      return;
    }

    self.getRevision(builder, revision, function(err, build) {
      if (err) {
        callback(err);
      } else if (!build) {
        if (force) {
          self.build(builder, revision, function(err) {
            if (err) {
              callback(err);
            } else {
              setTimeout(attempt, self._options.delay);
            }
          });
        } else {
          // The build was already forced, but isn't showing up - it is
          // *probably* pending. Alternatively, maybe num_builds happened
          // and we missed it. Oops.
          self.log.infof('build of ${revision} on ${builder} not available', {
            revision: revision,
            builder: builder
          });
          setTimeout(attempt, self._options.delay);
        }
      } else if (build.times[1] !== null) {
        self.log.infof('build ${number} on ${builder} finished: ${text}', {
          number: build.number,
          result_code: build.results || 0,
          builder: builder,
          text: build.text.join(' ')
        });
        // There is a Buildbot bug where build.results isn't included when it
        // is 0
        if ((build.results || 0) === 0) {
          callback(null, build);
        } else {
          callback(new Error(sprintf('Build %s of %s: %s', build.number, builder, build.text.join(' '))));
        }
      } else {
        self.log.infof('build ${number} on ${builder} ETA is ${eta}s', {
          number: build.number,
          builder: builder,
          eta: build.eta
        });
        // Try again in the greater of half of the ETA or the configured delay
        setTimeout(attempt, Math.max(build.eta * 500, self._options.delay));
      }
    });
  }

  attempt(true);
};

/** BuildBot Class */
exports.BuildBot = BuildBot;
<|endoftext|>


<commit_before>var http = require('http');
var https = require('https');
var url = require('url');
var extend = require('extend');
var Q = require('q');
var logger = require('./logger');

var events = {};
var TIMEOUT = 10000;
var INTERVAL = 1000 * 60 * 3;
var EEXCEED = 'EEXCEED';

function parseOptions(options) {
  var fullUrl = options.url || options.uri || options;
  if (typeof fullUrl === 'string') {
    options = extend(options, url.parse(fullUrl));
  }
  var maxLength = options.maxLength;
  if (!(maxLength > 0)) {
    options.maxLength = 0;
  }
  options.agent = false;
  options.rejectUnauthorized = false;
  return options;
}

function request(options, callback) {
  options = parseOptions(options);
  var isHttps = options.protocol === 'https:';
  var httpModule = isHttps ? https : http;
  var fullUrl = 'http' + (isHttps ? 's' : '') + '://' + options.host + options.path;
  var done, timer, res;
  var body = '';
  var callbackHandler = function(err) {
    clearTimeout(timer);
    if (err) {
      err && client.abort();
      logger.warn(fullUrl, err.message);
    }
    if (!done) {
      done = true;
      callback(err, body, res);
    }
  };
  var addTimeout = function() {
    clearTimeout(timer);
    timer = setTimeout(function() {
      callbackHandler(new Error('Timeout'));
    }, TIMEOUT);
  };
  addTimeout();
  var maxLength = options.maxLength;
  var client = httpModule.get(options, function(r) {
    res = r;
    res.on('error', callbackHandler);
    res.setEncoding('utf8');
    res.on('data', function(data) {
      body += data;
      addTimeout();
      if (maxLength && body.length > maxLength) {
        var err;
        if (!options.ignoreExceedError) {
          err = new Error('The response body exceeded length limit');
          err.code = EEXCEED;
        }
        callbackHandler(err);
      }
    });
    res.on('end', callbackHandler);
  });
  client.on('error', callbackHandler);
  client.end();
}

exports.request = request;

function updateRules(url) {
  var data = events[url];
  if (!data || data.promise) {
    return;
  }
  var defer = Q.defer();
  data.promise = defer;
  data = null;
  request({
    url: url,
    ignoreExceedError: true
  }, function(err, body, res) {
    data = events[url];
    if (!data) {
      return;
    }
    data.timer = setTimeout(updateRules, INTERVAL);
    body = body && body.trim();
    if ((err && err.code !== EEXCEED) || data.body === body) {
      return;
    }
    data.body = body;
    data.list.forEach(function(cb) {
      cb(body);
    });
  });
  return true;
}

exports.on = function(url, callback) {
  var data = events[url];
  if (data) {
    data.list.push(callback);
    if (data.body != null) {
      callback(data.body);
    }
    return;
  }
  events[url] = data = {};
  data.list = [callback];
  updateRules(url, data);
};

exports.off = function(url, callback) {
  var data = events[url];
  if (!data || !callback) {
    delete events[url];
    return;
  }
  var index = data.list.indexOf(callback);
  if (index === -1) {
    return;
  }
  data.list.splice(index, 1);
  if (!data.list.length) {
    clearTimeout(data.timer);
    delete events[url];
  }
};
<commit_msg>refactor: refine code<commit_after>var http = require('http');
var https = require('https');
var url = require('url');
var extend = require('extend');
var Q = require('q');
var logger = require('./logger');

var events = {};
var TIMEOUT = 10000;
var INTERVAL = 1000 * 60 * 3;
var EEXCEED = 'EEXCEED';

function parseOptions(options) {
  var fullUrl = options.url || options.uri || options;
  if (typeof fullUrl === 'string') {
    options = extend(options, url.parse(fullUrl));
  }
  var maxLength = options.maxLength;
  if (!(maxLength > 0)) {
    options.maxLength = 0;
  }
  options.agent = false;
  options.rejectUnauthorized = false;
  return options;
}

function request(options, callback) {
  options = parseOptions(options);
  var isHttps = options.protocol === 'https:';
  var httpModule = isHttps ? https : http;
  var fullUrl = 'http' + (isHttps ? 's' : '') + '://' + options.host + options.path;
  var done, timer, res;
  var body = '';
  var callbackHandler = function(err) {
    clearTimeout(timer);
    if (err) {
      err && client.abort();
      logger.warn(fullUrl, err.message);
    }
    if (!done) {
      done = true;
      callback(err, body, res);
    }
  };
  var addTimeout = function() {
    clearTimeout(timer);
    timer = setTimeout(function() {
      callbackHandler(new Error('Timeout'));
    }, TIMEOUT);
  };
  addTimeout();
  var maxLength = options.maxLength;
  var client = httpModule.get(options, function(r) {
    res = r;
    res.on('error', callbackHandler);
    res.setEncoding('utf8');
    res.on('data', function(data) {
      body += data;
      addTimeout();
      if (maxLength && body.length > maxLength) {
        var err;
        if (!options.ignoreExceedError) {
          err = new Error('The response body exceeded length limit');
          err.code = EEXCEED;
        }
        callbackHandler(err);
      }
    });
    res.on('end', callbackHandler);
  });
  client.on('error', callbackHandler);
  client.end();
}

exports.request = request;

function updateRules(url) {
  var data = events[url];
  if (!data || data.promise) {
    return;
  }
  var defer = Q.defer();
  data.promise = defer;
  data = null;
  request({
    url: url,
    ignoreExceedError: true
  }, function(err, body, res) {
    data = events[url];
    if (!data) {
      return;
    }
    data.timer = setTimeout(updateRules, INTERVAL);
    body = body && body.trim();
    if (err || data.body === body) {
      return;
    }
    data.body = body;
    data.list.forEach(function(cb) {
      cb(body);
    });
  });
  return true;
}

exports.on = function(url, callback) {
  var data = events[url];
  if (data) {
    data.list.push(callback);
    if (data.body != null) {
      callback(data.body);
    }
    return;
  }
  events[url] = data = {};
  data.list = [callback];
  updateRules(url, data);
};

exports.off = function(url, callback) {
  var data = events[url];
  if (!data || !callback) {
    delete events[url];
    return;
  }
  var index = data.list.indexOf(callback);
  if (index === -1) {
    return;
  }
  data.list.splice(index, 1);
  if (!data.list.length) {
    clearTimeout(data.timer);
    delete events[url];
  }
};
<|endoftext|>


<commit_before>'use babel'
/** @jsx dom */

import { Etch, Progress, view, dom } from './etch';
import Tooltip from './tooltip';

// Progress Bars
//
// This module provides an API for displaying progress bars in Atom. The methods
// below allow modifing the stack of progress bars, which is represented by
// corresponding UI elements:
//    - A "global" progress bar is shown in the status bar. In pratice, this is
//      the first determinate progress bar in the stack. If there is none, the
//      first element (which then is indeterminate) will be shown instead. If the
//      stack is empty, an empty progress bar will be shown instead.
//    - Hovering over that status bar element will show the complete stack as
//      an overlay.
//    - Hovering over the actual progress bar of one such stack element will show
//      the message correesponding to that progress bar, if there is any.
//
// Methods:
//
// add(p = {progress: 0})
//    Create and return a ProgressBar with the initial properties specified by `p`,
//    which has the following methods available to it:
//
//    p.level
//        Updates `p`s progress. `prog` is a number between 0 and 1 or `null`; if
//        it is `null`, an indeterminate progress bar will be displayed.
//
//    p.leftText, p.rightText, p.message
//        Sets the text displayed to the left of the progress bar, right of the
//        progress bar, or when hovering over it.
//
//    p.destroy()
//        Destroys `p` and removes it from the display stack.

function clamp(x, min, max) {
  return Math.min(Math.max(x, min), max);
}

class ProgressBar {
  constructor(level, {leftText}={}) {
    this._level = level;
    this._leftText = leftText;
  }
  destroy() {
    let i = stack.indexOf(this);
    if (i < 0) { return; }
    stack.splice(i, 1);
    update();
  }
}

['level', 'leftText', 'rightText', 'message'].forEach(key => {
  Object.defineProperty(ProgressBar.prototype, key, {
    get: function() { return this['_'+key] },
    set: function(val) {
      this['_'+key] = val;
      update();
    }
  });
});

class StackView extends Etch {
  get stack() { return this.children[0]; }
  update({}, [stack]) {
    if (stack.length > 0) super.update({}, [stack]);
  }
  render() {
    return
{
      this.stack.slice().reverse().map(({leftText, rightText, level}) =>

          {leftText}
          <Progress level={level} />
          {rightText}

      )
    }
;
  }
}

let stack = [];

function globalLevel() {
  if (stack.length == 0) return 0;
  let global = stack.find(({level})=>level!=null);
  if (global && global.level > 0.01) return global.level;
}

function update() {
  if (stack.length == 0) tooltip.hide();
  overlay.update();
  tileView.update();
}

export function activate() {
  if (this.activated) return;
  this.activated = true;

  overlay = view(() => <StackView>{stack}</StackView>);
  tileView = view(() =>
    <Progress level={globalLevel()} />
  );
  tooltip = new Tooltip(tileView.element, overlay.element, {cond: () => stack.length});

  if (statusBar) {
    tile = statusBar.addLeftTile({item: tileView.element, priority: -1});
  }
}

export function deactivate() {
  this.activated = false;
  overlay.destroy();
  tileView.destroy();
  tooltip.destroy();
  if (tile) tile.destroy();
}

export function consumeStatusBar(bar) {
  statusBar = bar;
}

export function add(prog, opts) {
  activate();
  let p = new ProgressBar(prog, opts);
  stack.push(p);
  update();
  return p;
}
<commit_msg>explicit globals<commit_after>'use babel'
/** @jsx dom */

import { Etch, Progress, view, dom } from './etch';
import Tooltip from './tooltip';

// Progress Bars
//
// This module provides an API for displaying progress bars in Atom. The methods
// below allow modifing the stack of progress bars, which is represented by
// corresponding UI elements:
//    - A "global" progress bar is shown in the status bar. In pratice, this is
//      the first determinate progress bar in the stack. If there is none, the
//      first element (which then is indeterminate) will be shown instead. If the
//      stack is empty, an empty progress bar will be shown instead.
//    - Hovering over that status bar element will show the complete stack as
//      an overlay.
//    - Hovering over the actual progress bar of one such stack element will show
//      the message correesponding to that progress bar, if there is any.
//
// Methods:
//
// add(p = {progress: 0})
//    Create and return a ProgressBar with the initial properties specified by `p`,
//    which has the following methods available to it:
//
//    p.level
//        Updates `p`s progress. `prog` is a number between 0 and 1 or `null`; if
//        it is `null`, an indeterminate progress bar will be displayed.
//
//    p.leftText, p.rightText, p.message
//        Sets the text displayed to the left of the progress bar, right of the
//        progress bar, or when hovering over it.
//
//    p.destroy()
//        Destroys `p` and removes it from the display stack.

let stack = [];
let statusBar, overlay, tileView, tooltip, tile, activated;

function clamp(x, min, max) {
  return Math.min(Math.max(x, min), max);
}

class ProgressBar {
  constructor(level, {leftText}={}) {
    this._level = level;
    this._leftText = leftText;
  }
  destroy() {
    let i = stack.indexOf(this);
    if (i < 0) { return; }
    stack.splice(i, 1);
    update();
  }
}

['level', 'leftText', 'rightText', 'message'].forEach(key => {
  Object.defineProperty(ProgressBar.prototype, key, {
    get: function() { return this['_'+key] },
    set: function(val) {
      this['_'+key] = val;
      update();
    }
  });
});

class StackView extends Etch {
  get stack() { return this.children[0]; }
  update({}, [stack]) {
    if (stack.length > 0) super.update({}, [stack]);
  }
  render() {
    return
{
      this.stack.slice().reverse().map(({leftText, rightText, level}) =>

          {leftText}
          <Progress level={level} />
          {rightText}

      )
    }
;
  }
}

function globalLevel() {
  if (stack.length == 0) return 0;
  let global = stack.find(({level})=>level!=null);
  if (global && global.level > 0.01) return global.level;
}

function update() {
  if (stack.length == 0) tooltip.hide();
  overlay.update();
  tileView.update();
}

export function activate() {
  if (activated) return;
  activated = true;

  overlay = view(() => <StackView>{stack}</StackView>);
  tileView = view(() =>
    <Progress level={globalLevel()} />
  );
  tooltip = new Tooltip(tileView.element, overlay.element, {cond: () => stack.length});

  if (statusBar) {
    tile = statusBar.addLeftTile({item: tileView.element, priority: -1});
  }
}

export function deactivate() {
  activated = false;
  overlay.destroy();
  tileView.destroy();
  tooltip.destroy();
  if (tile) tile.destroy();
}

export function consumeStatusBar(bar) {
  statusBar = bar;
}

export function add(prog, opts) {
  activate();
  let p = new ProgressBar(prog, opts);
  stack.push(p);
  update();
  return p;
}
<|endoftext|>


<commit_before>'use babel'
/** @jsx dom */

import { Etch, Progress, view, dom } from './etch';
import Tooltip from './tooltip';

// Progress Bars
//
// This module provides an API for displaying progress bars in Atom. The methods
// below allow modifing the stack of progress bars, which is represented by
// corresponding UI elements:
//    - A "global" progress bar is shown in the status bar. In pratice, this is
//      the first determinate progress bar in the stack. If there is none, the
//      first element (which then is indeterminate) will be shown instead. If the
//      stack is empty, an empty progress bar will be shown instead.
//    - Hovering over that status bar element will show the complete stack as
//      an overlay.
//    - Hovering over the actual progress bar of one such stack element will show
//      the message correesponding to that progress bar, if there is any.
//
// Methods:
//
// add(p = {progress: 0})
//    Create and return a ProgressBar with the initial properties specified by `p`,
//    which has the following methods available to it:
//
//    p.setProgress(prog)
//        Updates `p`s progress. `prog` is a number between 0 and 1 or `null`; if
//        it is `null`, an indeterminate progress bar will be displayed.
//
//    p.setLeftText(t), p.setRightText(t), p.setMessage(t)
//        Sets the text displayed to the left of the progress bar, right of the
//        progress bar, or when hovering over it.
//
//    p.destroy()
//        Destroys `p` and removes it from the display stack.

class StackView extends Etch {
  get stack() { return this.children[0]; }
  update({}, [stack]) {
    if (stack.length > 0) super.update({}, [stack]);
  }
  render() {
    return
{
      this.stack.slice().reverse().map(({leftText, rightText, level}) =>

          {leftText}
          <Progress level={level} />
          {rightText}

      )
    }
;
  }
}

let stack = [];

function globalLevel() {
  if (stack.length == 0) return 0;
  let global = stack.find(({level})=>level!=null);
  if (global && global.level > 0.01) return global.level;
}

function update() {
  if (stack.length == 0) tooltip.hide();
  overlay.update();
  tileView.update();
}

export function activate() {
  if (this.activated) return;
  this.activated = true;

  overlay = view(() => <StackView>{stack}</StackView>);
  tileView = view(() =>
    <Progress level={globalLevel()} />
  );
  tooltip = new Tooltip(tileView.element, overlay.element, {cond: () => stack.length});

  if (this.statusBar) {
    tile = this.statusBar.addLeftTile({item: tileView.element, priority: -1});
  }
}

export function deactivate() {
  this.activated = false;
  overlay.destroy();
  tileView.destroy();
  tooltip.destroy();
  if (tile) tile.destroy();
}

export function consumeStatusBar(bar) {
  return this.statusBar = bar;
}

export function create(p = {level: 0}) {
  this.activate();

  p.setProgress = prog => {
    p.level = prog;
    update();
  };
  p.setLeftText = t => {
    p.leftText = t;
    update();
  };
  p.setRightText = t => {
    p.rightText = t;
    update();
  };
  p.setMessage = t => {
    p.msg = t;
    update();
  };
  p.destroy = () => {
    let i = stack.indexOf(p);
    if (i < 0) { return; }
    stack.splice(i, 1);
    update();
  };
  p.register = () => {
    stack.push(p);
    update();
  };

  return p;
}

  // Public API
export function add(prog) {
  let p = create(prog);
  p.register();
  return p;
}
<commit_msg>refactor progress model<commit_after>'use babel'
/** @jsx dom */

import { Etch, Progress, view, dom } from './etch';
import Tooltip from './tooltip';

// Progress Bars
//
// This module provides an API for displaying progress bars in Atom. The methods
// below allow modifing the stack of progress bars, which is represented by
// corresponding UI elements:
//    - A "global" progress bar is shown in the status bar. In pratice, this is
//      the first determinate progress bar in the stack. If there is none, the
//      first element (which then is indeterminate) will be shown instead. If the
//      stack is empty, an empty progress bar will be shown instead.
//    - Hovering over that status bar element will show the complete stack as
//      an overlay.
//    - Hovering over the actual progress bar of one such stack element will show
//      the message correesponding to that progress bar, if there is any.
//
// Methods:
//
// add(p = {progress: 0})
//    Create and return a ProgressBar with the initial properties specified by `p`,
//    which has the following methods available to it:
//
//    p.level
//        Updates `p`s progress. `prog` is a number between 0 and 1 or `null`; if
//        it is `null`, an indeterminate progress bar will be displayed.
//
//    p.leftText, p.rightText, p.message
//        Sets the text displayed to the left of the progress bar, right of the
//        progress bar, or when hovering over it.
//
//    p.destroy()
//        Destroys `p` and removes it from the display stack.

function clamp(x, min, max) {
  return Math.min(Math.max(x, min), max);
}

class ProgressBar {
  constructor(level, {leftText}={}) {
    this._level = level;
    this._leftText = leftText;
  }
  destroy() {
    let i = stack.indexOf(this);
    if (i < 0) { return; }
    stack.splice(i, 1);
    update();
  }
}

['level', 'leftText', 'rightText', 'message'].forEach(key => {
  Object.defineProperty(ProgressBar.prototype, key, {
    get: function() { return this['_'+key] },
    set: function(val) {
      this['_'+key] = val;
      update();
    }
  });
});

class StackView extends Etch {
  get stack() { return this.children[0]; }
  update({}, [stack]) {
    if (stack.length > 0) super.update({}, [stack]);
  }
  render() {
    return
{
      this.stack.slice().reverse().map(({leftText, rightText, level}) =>

          {leftText}
          <Progress level={level} />
          {rightText}

      )
    }
;
  }
}

let stack = [];

function globalLevel() {
  if (stack.length == 0) return 0;
  let global = stack.find(({level})=>level!=null);
  if (global && global.level > 0.01) return global.level;
}

function update() {
  if (stack.length == 0) tooltip.hide();
  overlay.update();
  tileView.update();
}

export function activate() {
  if (this.activated) return;
  this.activated = true;

  overlay = view(() => <StackView>{stack}</StackView>);
  tileView = view(() =>
    <Progress level={globalLevel()} />
  );
  tooltip = new Tooltip(tileView.element, overlay.element, {cond: () => stack.length});

  if (statusBar) {
    tile = statusBar.addLeftTile({item: tileView.element, priority: -1});
  }
}

export function deactivate() {
  this.activated = false;
  overlay.destroy();
  tileView.destroy();
  tooltip.destroy();
  if (tile) tile.destroy();
}

export function consumeStatusBar(bar) {
  statusBar = bar;
}

export function add(prog, opts) {
  activate();
  let p = new ProgressBar(prog, opts);
  stack.push(p);
  update();
  return p;
}
<|endoftext|>


<commit_before>'use strict';

function visit(vertex, fn, visited, path) {
  var name = vertex.name,
      vertices = vertex.incoming,
      names = vertex.incomingNames,
      len = names.length,
      i;
  if (!visited) {
    visited = {};
  }
  if (!path) {
    path = [];
  }
  if (visited.hasOwnProperty(name)) {
    return;
  }
  path.push(name);
  visited[name] = true;
  for (i = 0; i < len; i++) {
    visit(vertices[names[i]], fn, visited, path);
  }
  fn(vertex, path);
  path.pop();
}

function DAG() {
  this.names = [];
  this.vertices = {};
}

DAG.prototype.add = function(name) {
  if (!name) { return; }
  if (this.vertices.hasOwnProperty(name)) {
    return this.vertices[name];
  }
  var vertex = {
    name: name,
    incoming: {},
    incomingNames: [],
    hasOutgoing: false,
    value: null
  };

  this.vertices[name] = vertex;
  this.names.push(name);
  return vertex;
};

DAG.prototype.map = function(name, value) {
  this.add(name).value = value;
};

DAG.prototype.addEdge = function(fromName, toName) {
  if (!fromName || !toName || fromName === toName) {
    return;
  }
  var from = this.add(fromName), to = this.add(toName);
  if (to.incoming.hasOwnProperty(fromName)) {
    return;
  }
  function checkCycle(vertex, path) {
    if (vertex.name === toName) {
      throw new Error('cycle detected: ' + toName + ' <- ' + path.join(' <- '));
    }
  }
  visit(from, checkCycle);
  from.hasOutgoing = true;
  to.incoming[fromName] = from;
  to.incomingNames.push(fromName);
};

DAG.prototype.topsort = function(fn) {
  var visited = {},
      vertices = this.vertices,
      names = this.names,
      len = names.length,
      i, vertex;
  for (i = 0; i < len; i++) {
    vertex = vertices[names[i]];
    if (!vertex.hasOutgoing) {
      visit(vertex, fn, visited);
    }
  }
};

DAG.prototype.addEdges = function(name, value, before, after) {
  var i;
  this.map(name, value);
  if (before) {
    if (typeof before === 'string') {
      this.addEdge(name, before);
    } else {
      for (i = 0; i < before.length; i++) {
        this.addEdge(name, before[i]);
      }
    }
  }
  if (after) {
    if (typeof after === 'string') {
      this.addEdge(after, name);
    } else {
      for (i = 0; i < after.length; i++) {
        this.addEdge(after[i], name);
      }
    }
  }
};

module.exports = DAG;
<commit_msg>utilities/DAG: Add doc comments<commit_after>'use strict';

function visit(vertex, fn, visited, path) {
  var name = vertex.name,
      vertices = vertex.incoming,
      names = vertex.incomingNames,
      len = names.length,
      i;
  if (!visited) {
    visited = {};
  }
  if (!path) {
    path = [];
  }
  if (visited.hasOwnProperty(name)) {
    return;
  }
  path.push(name);
  visited[name] = true;
  for (i = 0; i < len; i++) {
    visit(vertices[names[i]], fn, visited, path);
  }
  fn(vertex, path);
  path.pop();
}

/**
 * Directed acyclic graph.
 *
 * see [Wikipedia](https://en.wikipedia.org/wiki/Directed_acyclic_graph).
 *
 * @class DAG
 * @constructor
 */
function DAG() {
  /**
   * Array of all known vertex names.
   *
   * @final
   * @property names
   * @type Array
   */
  this.names = [];

  /**
   * Hash of vertices keyed by name.
   *
   * @final
   * @property vertices
   * @type Object
   */
  this.vertices = {};
}

/**
 * Adds a new vertex to the graph.
 *
 * @method add
 * @param {String} name Name of the vertex
 * @return {Object|undefined} The new vertex or the already existing vertex with the same name
 */
DAG.prototype.add = function(name) {
  if (!name) { return; }
  if (this.vertices.hasOwnProperty(name)) {
    return this.vertices[name];
  }
  var vertex = {
    name: name,
    incoming: {},
    incomingNames: [],
    hasOutgoing: false,
    value: null
  };

  this.vertices[name] = vertex;
  this.names.push(name);
  return vertex;
};

/**
 * Assigns `value` to the vertex named `name`.
 *
 * Creates a new vertex if a vertex named `name` does not exist yet.
 *
 * @method map
 * @param {String} name Name of the vertex
 * @param {any} value Value of the vertex
 */
DAG.prototype.map = function(name, value) {
  this.add(name).value = value;
};

/**
 * Adds an edge between two vertices.
 *
 * @method addEdge
 * @param fromName Name of the `from` vertex
 * @param toName Name of the `to` vertex
 * @throws {Error} if a cycle is detected
 */
DAG.prototype.addEdge = function(fromName, toName) {
  if (!fromName || !toName || fromName === toName) {
    return;
  }
  var from = this.add(fromName), to = this.add(toName);
  if (to.incoming.hasOwnProperty(fromName)) {
    return;
  }
  function checkCycle(vertex, path) {
    if (vertex.name === toName) {
      throw new Error('cycle detected: ' + toName + ' <- ' + path.join(' <- '));
    }
  }
  visit(from, checkCycle);
  from.hasOutgoing = true;
  to.incoming[fromName] = from;
  to.incomingNames.push(fromName);
};

/**
 * Traverses the graph depth-first.
 *
 * Each vertex will only be visited once even if it has multiple parent vertices.
 *
 * @method topsort
 * @param {Function} fn Function that is called with each vertex and the path to it
 */
DAG.prototype.topsort = function(fn) {
  var visited = {},
      vertices = this.vertices,
      names = this.names,
      len = names.length,
      i, vertex;
  for (i = 0; i < len; i++) {
    vertex = vertices[names[i]];
    if (!vertex.hasOutgoing) {
      visit(vertex, fn, visited);
    }
  }
};

/**
 * Assigns `value` to the vertex named `name` and creates the edges defined by `before` and `after`.
 *
 * @method addEdges
 * @param {String} name Name of the vertex
 * @param {any} value Value of the vertex
 * @param {String|Array} before Adds edges from `name` to each of `before`
 * @param {String|Array} after Adds edges from each of `after` to `name`
 */
DAG.prototype.addEdges = function(name, value, before, after) {
  var i;
  this.map(name, value);
  if (before) {
    if (typeof before === 'string') {
      this.addEdge(name, before);
    } else {
      for (i = 0; i < before.length; i++) {
        this.addEdge(name, before[i]);
      }
    }
  }
  if (after) {
    if (typeof after === 'string') {
      this.addEdge(after, name);
    } else {
      for (i = 0; i < after.length; i++) {
        this.addEdge(after[i], name);
      }
    }
  }
};

module.exports = DAG;
<|endoftext|>


<commit_before>import EventEmitter from 'events'

import bfj from 'bfj-node4'
import { isObject } from 'lodash'
import figures from 'figures'
import moment from 'moment'

import getEntityName from './get-entity-name'

export const logEmitter = new EventEmitter()

function extractErrorInformation (error) {
  try {
    const data = JSON.parse(error.message)
    if (isObject(data)) {
      return data
    }
  } catch (err) {
    throw new Error('Unable to extract API error data')
  }
}

export function formatLogMessageOneLine (logMessage) {
  const { level } = logMessage
  if (!level) {
    return logMessage.toString().replace(/\s+/g, ' ')
  }
  if (level === 'info') {
    return logMessage.info
  }
  if (level === 'warning') {
    return logMessage.warning
  }
  try {
    // Display enhanced API error message when available
    const errorOutput = []
    const data = extractErrorInformation(logMessage.error)
    if ('status' in data || 'statusText' in data) {
      const status = [data.status, data.statusText].filter((a) => a).join(' - ')
      errorOutput.push(`Status: ${status}`)
    }
    if ('message' in data) {
      errorOutput.push(`Message: ${data.message}`)
    }
    if ('entity' in data) {
      errorOutput.push(`Entity: ${getEntityName(data.entity)}`)
    }
    if ('details' in data && 'errors' in data.details) {
      const errorList = data.details.errors.map((error) => error.details || error.name)
      errorOutput.push(`Details: ${errorList.join(', ')}`)
    }
    if ('requestId' in data) {
      errorOutput.push(`Request ID: ${data.requestId}`)
    }
    return `${logMessage.error.name}: ${errorOutput.join(' - ')}`
  } catch (err) {
    // Fallback for errors without API information
    return logMessage.error.toString().replace(/\s+/g, ' ')
  }
}

export function formatLogMessageLogfile (logMessage) {
  const { level } = logMessage
  if (level === 'info' || level === 'warning') {
    return logMessage
  }
  if (!logMessage.error) {
    // Enhance node errors to logMessage format
    logMessage.error = logMessage
  }
  try {
    // Enhance error with extracted API error log
    const data = extractErrorInformation(logMessage.error)
    const errorOutput = Object.assign({}, logMessage.error, {data})
    delete errorOutput.message
    logMessage.error = errorOutput
  } catch (err) {
    // Fallback for errors without API information
    if (logMessage.error.stack) {
      logMessage.error.stacktrace = logMessage.error.stack.toString().split(/\n +at /)
    }
  }
  return logMessage
}

// Display all errors
export function displayErrorLog (errorLog) {
  if (errorLog.length) {
    const warningsCount = errorLog.filter((error) => error.hasOwnProperty('warning')).length
    const errorsCount = errorLog.filter((error) => error.hasOwnProperty('error')).length
    console.log(`\n\nThe following ${errorsCount} errors and ${warningsCount} warnings occurred:\n`)

    errorLog
      .map((logMessage) => `${moment(logMessage.ts).format('HH:mm:SS')} - ${formatLogMessageOneLine(logMessage)}`)
      .map((logMessage) => console.log(logMessage))

    return
  }
  console.log('No errors or warnings occurred')
}

// Write all log messages instead of infos to the error log file
export function writeErrorLogFile (destination, errorLog) {
  const logFileData = errorLog
    .map(formatLogMessageLogfile)
  return bfj.write(destination, logFileData, {
    circular: 'ignore',
    space: 2
  })
    .then(() => {
      console.log('\nStored the detailed error log file at:')
      console.log(destination)
    })
    .then(() => {
      const multiError = new Error('Errors occured')
      multiError.name = 'ContentfulMultiError'
      multiError.errors = errorLog
      throw multiError
    })
}

// Init listeners for log messages, transform them into proper format and logs/displays them
export function setupLogging (log) {
  function errorLogger (level, error) {
    const logMessage = {
      ts: (new Date()).toJSON(),
      level,
      [level]: error
    }
    if (level !== 'info') {
      log.push(logMessage)
    }
    logEmitter.emit('display', logMessage)
  }

  logEmitter.addListener('warning', (error) => errorLogger('warning', error))
  logEmitter.addListener('error', (error) => errorLogger('error', error))
  logEmitter.addListener('info', (error) => errorLogger('info', error))
}

// Format log message to display them as task status
export function logToTaskOutput (task) {
  function logToTask (logMessage) {
    const content = formatLogMessageOneLine(logMessage)
    const symbols = {
      'info': figures.tick,
      'warning': figures.warning,
      'error': figures.cross
    }
    task.output = `${symbols[logMessage.level]} ${content}`.trim()
  }

  logEmitter.on('display', logToTask)

  return () => {
    logEmitter.removeListener('display', logToTask)
  }
}
<commit_msg>feat(logging): add task run time to title when task completes<commit_after>import EventEmitter from 'events'

import bfj from 'bfj-node4'
import { isObject } from 'lodash'
import figures from 'figures'
import moment from 'moment'

import getEntityName from './get-entity-name'

export const logEmitter = new EventEmitter()

function extractErrorInformation (error) {
  try {
    const data = JSON.parse(error.message)
    if (isObject(data)) {
      return data
    }
  } catch (err) {
    throw new Error('Unable to extract API error data')
  }
}

export function formatLogMessageOneLine (logMessage) {
  const { level } = logMessage
  if (!level) {
    return logMessage.toString().replace(/\s+/g, ' ')
  }
  if (level === 'info') {
    return logMessage.info
  }
  if (level === 'warning') {
    return logMessage.warning
  }
  try {
    // Display enhanced API error message when available
    const errorOutput = []
    const data = extractErrorInformation(logMessage.error)
    if ('status' in data || 'statusText' in data) {
      const status = [data.status, data.statusText].filter((a) => a).join(' - ')
      errorOutput.push(`Status: ${status}`)
    }
    if ('message' in data) {
      errorOutput.push(`Message: ${data.message}`)
    }
    if ('entity' in data) {
      errorOutput.push(`Entity: ${getEntityName(data.entity)}`)
    }
    if ('details' in data && 'errors' in data.details) {
      const errorList = data.details.errors.map((error) => error.details || error.name)
      errorOutput.push(`Details: ${errorList.join(', ')}`)
    }
    if ('requestId' in data) {
      errorOutput.push(`Request ID: ${data.requestId}`)
    }
    return `${logMessage.error.name}: ${errorOutput.join(' - ')}`
  } catch (err) {
    // Fallback for errors without API information
    return logMessage.error.toString().replace(/\s+/g, ' ')
  }
}

export function formatLogMessageLogfile (logMessage) {
  const { level } = logMessage
  if (level === 'info' || level === 'warning') {
    return logMessage
  }
  if (!logMessage.error) {
    // Enhance node errors to logMessage format
    logMessage.error = logMessage
  }
  try {
    // Enhance error with extracted API error log
    const data = extractErrorInformation(logMessage.error)
    const errorOutput = Object.assign({}, logMessage.error, {data})
    delete errorOutput.message
    logMessage.error = errorOutput
  } catch (err) {
    // Fallback for errors without API information
    if (logMessage.error.stack) {
      logMessage.error.stacktrace = logMessage.error.stack.toString().split(/\n +at /)
    }
  }
  return logMessage
}

// Display all errors
export function displayErrorLog (errorLog) {
  if (errorLog.length) {
    const warningsCount = errorLog.filter((error) => error.hasOwnProperty('warning')).length
    const errorsCount = errorLog.filter((error) => error.hasOwnProperty('error')).length
    console.log(`\n\nThe following ${errorsCount} errors and ${warningsCount} warnings occurred:\n`)

    errorLog
      .map((logMessage) => `${moment(logMessage.ts).format('HH:mm:SS')} - ${formatLogMessageOneLine(logMessage)}`)
      .map((logMessage) => console.log(logMessage))

    return
  }
  console.log('No errors or warnings occurred')
}

// Write all log messages instead of infos to the error log file
export function writeErrorLogFile (destination, errorLog) {
  const logFileData = errorLog
    .map(formatLogMessageLogfile)
  return bfj.write(destination, logFileData, {
    circular: 'ignore',
    space: 2
  })
    .then(() => {
      console.log('\nStored the detailed error log file at:')
      console.log(destination)
    })
    .then(() => {
      const multiError = new Error('Errors occured')
      multiError.name = 'ContentfulMultiError'
      multiError.errors = errorLog
      throw multiError
    })
}

// Init listeners for log messages, transform them into proper format and logs/displays them
export function setupLogging (log) {
  function errorLogger (level, error) {
    const logMessage = {
      ts: (new Date()).toJSON(),
      level,
      [level]: error
    }
    if (level !== 'info') {
      log.push(logMessage)
    }
    logEmitter.emit('display', logMessage)
  }

  logEmitter.addListener('warning', (error) => errorLogger('warning', error))
  logEmitter.addListener('error', (error) => errorLogger('error', error))
  logEmitter.addListener('info', (error) => errorLogger('info', error))
}

// Format log message to display them as task status
export function logToTaskOutput (task) {
  function logToTask (logMessage) {
    const content = formatLogMessageOneLine(logMessage)
    const symbols = {
      'info': figures.tick,
      'warning': figures.warning,
      'error': figures.cross
    }
    task.output = `${symbols[logMessage.level]} ${content}`.trim()
  }

  const startTime = Date.now()

  logEmitter.on('display', logToTask)

  return () => {
    const seconds = Math.ceil((Date.now() - startTime) / 1000)
    task.title = `${task.title} (${seconds}s)`
    logEmitter.removeListener('display', logToTask)
  }
}
<|endoftext|>


<commit_before>
var log = require("../utils/log")
  , fs = require("./graceful-fs")
  , path = require("path")

module.exports = mkdir
function mkdir (ensure, cb) {
  ensure = ensure.replace(/\/+$/, '')
  if (ensure.charAt(0) !== "/") ensure = path.join(process.cwd(), ensure)
  var dirs = ensure.split("/")
    , walker = []
    , chmod = 0755
  walker.push(dirs.shift()) // gobble the "/" first
  ;(function S (d) {
    if (d === undefined) return cb()
    walker.push(d)
    var dir = walker.join("/")
    fs.stat(dir, function STATCB (er, s) {
      if (er) {
        fs.mkdir(dir, chmod, function MKDIRCB (er, s) {
          if (er && er.message.indexOf("EEXIST") === 0) {
            // When multiple concurrent actors are trying to ensure the same directories,
            // it can sometimes happen that something doesn't exist when you do the stat,
            // and then DOES exist when you try to mkdir.  In this case, just go back to
            // the stat to make sure it's a dir and not a file.
            return fs.stat(dir, STATCB)
          }
          if (er) return cb(er)
          S(dirs.shift())
        })
      } else {
        if (s.isDirectory()) S(dirs.shift())
        else cb(new Error("Failed to mkdir "+dir+": File exists"))
      }
    })
  })(dirs.shift())
}
<commit_msg>Specify a uid/gid to mkdir, and it'll chown it.<commit_after>
var log = require("./log")
  , fs = require("./graceful-fs")
  , path = require("path")
  , npm = require("../../npm")
  , exec = require("./exec")

module.exports = mkdir
function mkdir (ensure, uid, gid, cb) {
  if (!cb) cb = gid, gid = null
  if (!cb) cb = uid, uid = null
  uid = uid || npm.config.get("operator")
  gid = gid || npm.config.get("group")
  log.silly([ensure, uid, gid], "mkdir")

  // chown *only* takes numeric uid/gid
  if (!isNaN(uid)) uid = +uid
  if (!isNaN(gid)) gid = +gid
  if (isNaN(uid) || isNaN(gid)) {
    log.verbose([uid, gid], "Getting numeric uid,gid for mkdir")
    var getter = path.join(__dirname, "../../bin/npm-get-uid-gid.js")
    return exec( "node", [getter, uid, gid], process.env, false
               , process.cwd(), process.getuid(), process.getgid()
               , function (er, code, out, err) {
      if (er) return log.er(cb, "Could not set uid/gid "+err)(er)
      log.silly(out, "output from getuid/gid")
      out = JSON.parse(out+"")
      if (isNaN(out.uid) || isNaN(out.gid)) return cb(new Error(
        "mkdir: Could not get uid/gid: "+JSON.stringify(out)))
      mkdir(ensure, out.uid, out.gid, cb)
    })
  }
  log.verbose([uid, gid], "mkdir uid,gid")

  ensure = ensure.replace(/\/+$/, '')
  if (ensure.charAt(0) !== "/") ensure = path.join(process.cwd(), ensure)
  var dirs = ensure.split("/")
    , walker = []
    , chmod = 0755
  walker.push(dirs.shift()) // gobble the "/" first
  ;(function S (d) {
    if (d === undefined) {
      // now the directory has been created.
      // chown it to the desired uid/gid
      return fs.chown(ensure, uid, gid, cb)
    }
    walker.push(d)
    var dir = walker.join("/")
    fs.stat(dir, function STATCB (er, s) {
      if (er) {
        fs.mkdir(dir, chmod, function MKDIRCB (er, s) {
          if (er && er.message.indexOf("EEXIST") === 0) {
            // handle stat race
            return fs.stat(dir, STATCB)
          }
          if (er) return cb(er)
          S(dirs.shift())
        })
      } else {
        if (s.isDirectory()) S(dirs.shift())
        else cb(new Error("Failed to mkdir "+dir+": File exists"))
      }
    })
  })(dirs.shift())
}
<|endoftext|>


<commit_before>
module.exports = request;
request.GET = GET;
request.MULTIGET = MULTIGET;
request.PUT = PUT;
request.POST = POST;
request.uploadFile = uploadFile;
request.getFeedHenryUrl = getFeedHenryUrl;
request.getMessagingUrl = getMessagingUrl;
request.requestFunc = doRequest;

var fhc = require("../fhc");
var requestjs = require("request");
var url = require("url");
var util = require('util');
var log = require("./log");
var ini = require("./ini");
var fs = require("fs");
var os = require("os");
var keys = require("../keys");

function request(){};

function doRequest (fhurl, method, where, what, etag, nofollow, cb_) {
  log.silly(where||"/", method);

  if (where.match(/^\/?favicon.ico/)) {
    return cb_(new Error("favicon.ico isn't a package, it's a picture."));
  }
  if (typeof cb_ !== "function") cb_ = nofollow, nofollow = false;
  if (typeof cb_ !== "function") cb_ = etag, etag = null;
  if (typeof cb_ !== "function") cb_ = what, what = null;

  if (fhurl instanceof Error) return cb_(fhurl);
  log.silly(fhurl, "url, unparsed");

  // Since there are multiple places where an error could occur,
  // don't let the cb be called more than once.
  var errState = null;
  var __stats__ = {
    "url": where
  };
  function cb (er) {
    if (errState) return;
    if (er) errState = er;
    if (fhc.config.get("statsMode") && null != arguments[1]) {
      arguments[1].__stats__ = __stats__;
    }
    cb_.apply(undefined, arguments);
  }

  where = resolveUrl(where, fhurl);

  var remote = url.parse(where)
    , secure = remote.protocol === "https:"
    , port = remote.port || (secure ? 443 : 80)
    , hostname = remote.hostname;
  //  , auth = authRequired && fhc.config.get("_auth")

  log.verbose(remote.href + " payload: " + util.inspect(what), "calling url");
  if (port !== (secure ? 443 : 80)) hostname += ":" + port;
  log.silly(port, "port");

  var headers = getHeaders();

  if (what) {
    log.verbose(what, "what...");
    delete what._etag;
    log.silly(what,"writing json");
    if (typeof(what) === 'string') {
      what = new Buffer(what);
    }else {
      what = new Buffer(JSON.stringify(what));
    }
    headers["content-type"] = "application/json";
    headers["content-length"] = what.length;
  } else {
    headers["content-length"] = 0;
  }
  if (etag) {
    log.silly(etag, "etag");
    headers[method === "GET" ? "if-none-match" : "if-match"] = etag;
  }

  if (!remote.protocol) log.warn(remote, "No protocol?");

  log.silly(headers, "headers");

  var opts = { uri : where
             , method: method
             , body: what
             , headers: headers
             , proxy: getProxy(remote)
             , followRedirect : !nofollow
             };

  var startTime = Date.now();
  requestjs(opts, function (error, response, body) {
    var data = body;

    if( error ) {
      log.er(cb, "response error from "+where);
      cb();
    }

    __stats__.duration = Date.now() - startTime;
    __stats__.status = response.statusCode;
    __stats__.body = data;

    var parsed;
    if (response.statusCode !== 304) {
      //console.log("HEADERS: " + util.inspect(response.headers['content-type']));
      //console.log("DATA: " + data);
      log.silly(response.headers, "response headers");
      var contentType = response.headers ? response.headers['content-type'] : '';
      if (contentType && (contentType.indexOf('application/json') != -1 || contentType.indexOf('text/json') != -1 || contentType.indexOf('text/plain') != -1)) {
        try {
          parsed = JSON.parse(data);
          if (response.headers.etag) {
            // NOTE: not showing etags any longer in returned json (see 4712)
            // parsed._etag = response.headers.etag;
          }
        } catch (ex) {
          ex.message += "\n" + data;
          log.verbose(data, "bad json");
          log.error("error parsing json", "FeedHenry");
          return cb(ex, null, data, response);
        }
      }else {
        parsed = data;
      }
    }

    var er = undefined;

    if (parsed && parsed.error) {
      var w = url.parse(where).pathname.substr(1)
        , name = w.split("/")[0];
      if (parsed.error === "not_found") {
        er = new Error("404 Not Found: "+name);
      } else {
        er = parsed.error + " " + (parsed.reason || "");
      }
    } else if (method !== "HEAD" && method !== "GET") {
      // invalidate cache
      // This is irrelevant for commands that do etag caching, but
      // ls and view also have a timed cache, so this keeps the user
      // from thinking that it didn't work when it did.
      // Note that failure is an acceptable option here, since the
      // only result will be a stale cache for some helper commands.
      var path = require("path")
        , p = url.parse(where).pathname.split("/")
        , _ = "/"
        , caches = p.map(function (part) {
            return _ = path.join(_, part);
          }).map(function (cache) {
            return path.join(fhc.cache, cache, ".cache.json");
          });
      // TODO asyncMap(caches, rm, function () {});
    }
    return cb(er, parsed, data, response);
  })

}

function GET (url, where, etag, nofollow, cb) {
  request.requestFunc(url, "GET", where, null, etag, nofollow, cb);
}

function PUT (url, where, what, etag, nofollow, cb) {
  request.requestFunc(url, "PUT", where, what, etag, nofollow, cb);
}

function POST (url, where, what, etag, nofollow, cb) {
  request.requestFunc(url, "POST", where, what, etag, nofollow, cb);
}

function MULTIGET (fhurls, where, cb) {
  var urls = fhurls.split(',');
  var ret = new Object();

  function doGetUrl(url, callback) {
    request.requestFunc(url, "GET", where, null, null, function(er, parsed, data, response) {
      callback(er, data);
    });
  }

  async.map(urls, doGetUrl, function(err, results) {
    if (err) return cb(err);
    for (var i=0; i<urls.length; i++) {
      ret[urls[i]] = results[i];
    }
    return cb(err, ret, ret, ret);
  });
}

function uploadFile(fhurl, filepath, fields, contentType, cb){
  var boundary = Math.random();
  var post_data = [];
  var path = require('path');
  var filename = path.basename(filepath);

  var fullUrl = resolveUrl(fhurl);
  var remote = url.parse(fullUrl);
  var headers = getHeaders();

  var opts = {
              url : fullUrl
            , headers : headers
            , proxy : getProxy(remote)
  };
  var r = requestjs.post(opts);

  // Get a handle on a form for Multi Part Form Upload
  var form = r.form();

  // Add all passed fields to the form
  for(var key in fields){

    // Dirty hack to get around the limitations of CombinedStream.isStreamLike()...
    // i.e. uit thinks booleans are streams!
    var value = fields[key] + "";
    log.verbose('Adding form field ' + key + " with value " + value);
    form.append(key, value);
  }

  // Add the file to the form
  form.append('file', fs.createReadStream(filepath));

  // Get the content length of the form once it is finished adding all fields.
  form.getLength(function(err, length) {
    if(err) cb(err);

    log.verbose(length, "form.getLength()");

    headers["content-length"] = length;

    // Set the headers on the request object
    r.setHeaders(headers);
  });

  r.on("error", function (er) {
    log.er(er);
    cb(er);
  })

  r.on('response', function(response){

    log.silly('received response');
    response.setEncoding('utf8');

    var data = '';
    response.on('data', function(chunk){
      data = data + chunk;
    });

    response.on('end', function(){
      log.silly(data, "DATA");
      data = JSON.parse(data);
      return cb(undefined, data);
    });

    response.on('error', function(err) {
      return cb(err);
    });
  });

  return r;
}

function File (name, cb) {
  var f = this;
  f.name = name;
  if (f.loaded) return cb(undefined, f);
  log.info(f.name, "stat");
  fs.stat(f.name, function (er, stat) {
    if (er) return log.er(cb, "doesn't exist "+f.name)(er);
    log.silly(stat, "stat "+name);
    f.length = stat.size;
    fs.open(f.name, "r", 0666, function (er, fd) {
      if (er) return log.er(cb, "Error opening "+f.name)(er);
      f.fd = fd;
      cb(undefined, f);
    });
  });
};

function getFeedHenryUrl () {
  var r = fhc.config.get("feedhenry");
  log.verbose(r, "feedhenry url");
  if (!r) {
    return new Error("Must define feedhenry URL before accessing FeedHenry.");
  }
  if (r.substr(-1) !== "/") r += "/";
  fhc.config.set("feedhenry", r);
  return r;
};

function getMessagingUrl () {
  var r = fhc.config.get("messaging");
  if (!r) {
    return new Error("Must define messaging URL before accessing FeedHenry Message Server.");
  }
  if (r.substr(-1) !== "/") r += "/";
  fhc.config.set("messaging", r);
  return r;
}

function resolveUrl(where, fhurl) {
  log.verbose(where, "resolveUrl - IN");
  if (!where.match(/^https?:\/\//)) {
    if (where.charAt(0) !== "/") where = "/" + where;
    fhurl = fhurl || getFeedHenryUrl();
    where = url.resolve(fhurl, where);
  }

  log.verbose(where, "resolveUrl - OUT");

  return where;
}

function getHeaders() {
  var headers = { "accept" : "application/json" };
  var cookie = fhc.config.get("cookie");
  if (cookie != undefined) {
    headers.cookie = "feedhenry=" + cookie + ";";
    log.silly(headers.cookie, "cookie");
  }

  log.silly(fhc.config.get('nocache'), "nocache");
  if (fhc.config.get('nocache')) {
    headers['X-FeedHenry-CacheOff'] = "script_super";
  }
  try {
    var explainLvl = parseInt(fhc.config.get('explain'), 10);
    log.silly(explainLvl, "explain level");
    if (explainLvl > 0) {
      headers['X-FeedHenry-Explain'] = 'active' + ((explainLvl > 1) ? ' stacktrace' : '');
    }
  } catch (e) {
    log.error('explain config value should be a Number: ' + e);
  }
  var platform = '<node 0.4>';
  var release = '<node 0.4>';
  try{
    platform = os.platform();
    release = os.release();
  } catch (x) {
    // ignored, this will fail on node 0.4.x
  }
  headers['User-Agent'] = "FHC/" + fhc.version + ' ' + platform + '/' + release;

  var apiKey = keys.getUserApiKey();
  if(null != apiKey && (typeof apiKey !== "undefined")){
    headers['X-FH-AUTH-USER'] = apiKey;
  }

  log.verbose(headers, "getHeaders");
  return headers;
}

function getProxy(url) {
  var proxy
  if (url.protocol !== "https:" || !(proxy = fhc.config.get("https-proxy"))) {
    proxy = fhc.config.get("proxy")
  }
  log.silly(proxy, "getProxy");
  return proxy;
}<commit_msg>Adding missing content type header for file upload<commit_after>
module.exports = request;
request.GET = GET;
request.MULTIGET = MULTIGET;
request.PUT = PUT;
request.POST = POST;
request.uploadFile = uploadFile;
request.getFeedHenryUrl = getFeedHenryUrl;
request.getMessagingUrl = getMessagingUrl;
request.requestFunc = doRequest;

var fhc = require("../fhc");
var requestjs = require("request");
var url = require("url");
var util = require('util');
var log = require("./log");
var ini = require("./ini");
var fs = require("fs");
var os = require("os");
var keys = require("../keys");

function request(){};

function doRequest (fhurl, method, where, what, etag, nofollow, cb_) {
  log.silly(where||"/", method);

  if (where.match(/^\/?favicon.ico/)) {
    return cb_(new Error("favicon.ico isn't a package, it's a picture."));
  }
  if (typeof cb_ !== "function") cb_ = nofollow, nofollow = false;
  if (typeof cb_ !== "function") cb_ = etag, etag = null;
  if (typeof cb_ !== "function") cb_ = what, what = null;

  if (fhurl instanceof Error) return cb_(fhurl);
  log.silly(fhurl, "url, unparsed");

  // Since there are multiple places where an error could occur,
  // don't let the cb be called more than once.
  var errState = null;
  var __stats__ = {
    "url": where
  };
  function cb (er) {
    if (errState) return;
    if (er) errState = er;
    if (fhc.config.get("statsMode") && null != arguments[1]) {
      arguments[1].__stats__ = __stats__;
    }
    cb_.apply(undefined, arguments);
  }

  where = resolveUrl(where, fhurl);

  var remote = url.parse(where)
    , secure = remote.protocol === "https:"
    , port = remote.port || (secure ? 443 : 80)
    , hostname = remote.hostname;
  //  , auth = authRequired && fhc.config.get("_auth")

  log.verbose(remote.href + " payload: " + util.inspect(what), "calling url");
  if (port !== (secure ? 443 : 80)) hostname += ":" + port;
  log.silly(port, "port");

  var headers = getHeaders();

  if (what) {
    log.verbose(what, "what...");
    delete what._etag;
    log.silly(what,"writing json");
    if (typeof(what) === 'string') {
      what = new Buffer(what);
    }else {
      what = new Buffer(JSON.stringify(what));
    }
    headers["content-type"] = "application/json";
    headers["content-length"] = what.length;
  } else {
    headers["content-length"] = 0;
  }
  if (etag) {
    log.silly(etag, "etag");
    headers[method === "GET" ? "if-none-match" : "if-match"] = etag;
  }

  if (!remote.protocol) log.warn(remote, "No protocol?");

  log.silly(headers, "headers");

  var opts = { uri : where
             , method: method
             , body: what
             , headers: headers
             , proxy: getProxy(remote)
             , followRedirect : !nofollow
             };

  var startTime = Date.now();
  requestjs(opts, function (error, response, body) {
    var data = body;

    if( error ) {
      log.er(cb, "response error from "+where);
      cb();
    }

    __stats__.duration = Date.now() - startTime;
    __stats__.status = response.statusCode;
    __stats__.body = data;

    var parsed;
    if (response.statusCode !== 304) {
      //console.log("HEADERS: " + util.inspect(response.headers['content-type']));
      //console.log("DATA: " + data);
      log.silly(response.headers, "response headers");
      var contentType = response.headers ? response.headers['content-type'] : '';
      if (contentType && (contentType.indexOf('application/json') != -1 || contentType.indexOf('text/json') != -1 || contentType.indexOf('text/plain') != -1)) {
        try {
          parsed = JSON.parse(data);
          if (response.headers.etag) {
            // NOTE: not showing etags any longer in returned json (see 4712)
            // parsed._etag = response.headers.etag;
          }
        } catch (ex) {
          ex.message += "\n" + data;
          log.verbose(data, "bad json");
          log.error("error parsing json", "FeedHenry");
          return cb(ex, null, data, response);
        }
      }else {
        parsed = data;
      }
    }

    var er = undefined;

    if (parsed && parsed.error) {
      var w = url.parse(where).pathname.substr(1)
        , name = w.split("/")[0];
      if (parsed.error === "not_found") {
        er = new Error("404 Not Found: "+name);
      } else {
        er = parsed.error + " " + (parsed.reason || "");
      }
    } else if (method !== "HEAD" && method !== "GET") {
      // invalidate cache
      // This is irrelevant for commands that do etag caching, but
      // ls and view also have a timed cache, so this keeps the user
      // from thinking that it didn't work when it did.
      // Note that failure is an acceptable option here, since the
      // only result will be a stale cache for some helper commands.
      var path = require("path")
        , p = url.parse(where).pathname.split("/")
        , _ = "/"
        , caches = p.map(function (part) {
            return _ = path.join(_, part);
          }).map(function (cache) {
            return path.join(fhc.cache, cache, ".cache.json");
          });
      // TODO asyncMap(caches, rm, function () {});
    }
    return cb(er, parsed, data, response);
  })

}

function GET (url, where, etag, nofollow, cb) {
  request.requestFunc(url, "GET", where, null, etag, nofollow, cb);
}

function PUT (url, where, what, etag, nofollow, cb) {
  request.requestFunc(url, "PUT", where, what, etag, nofollow, cb);
}

function POST (url, where, what, etag, nofollow, cb) {
  request.requestFunc(url, "POST", where, what, etag, nofollow, cb);
}

function MULTIGET (fhurls, where, cb) {
  var urls = fhurls.split(',');
  var ret = new Object();

  function doGetUrl(url, callback) {
    request.requestFunc(url, "GET", where, null, null, function(er, parsed, data, response) {
      callback(er, data);
    });
  }

  async.map(urls, doGetUrl, function(err, results) {
    if (err) return cb(err);
    for (var i=0; i<urls.length; i++) {
      ret[urls[i]] = results[i];
    }
    return cb(err, ret, ret, ret);
  });
}

function uploadFile(fhurl, filepath, fields, contentType, cb){
  var boundary = Math.random();
  var post_data = [];
  var path = require('path');
  var filename = path.basename(filepath);

  var fullUrl = resolveUrl(fhurl);
  var remote = url.parse(fullUrl);
  var headers = getHeaders();
  headers["content-type"] = contentType;

  var opts = {
              url : fullUrl
            , headers : headers
            , proxy : getProxy(remote)
  };

  log.verbose(opts, "uploadFile - opts");

  var r = requestjs.post(opts);

  // Get a handle on a form for Multi Part Form Upload
  var form = r.form();

  // Add all passed fields to the form
  for(var key in fields){

    // Dirty hack to get around the limitations of CombinedStream.isStreamLike()...
    // i.e. uit thinks booleans are streams!
    var value = fields[key] + "";
    log.verbose('Adding form field ' + key + " with value " + value);
    form.append(key, value);
  }

  // Add the file to the form
  form.append('file', fs.createReadStream(filepath));

  // Get the content length of the form once it is finished adding all fields.
  form.getLength(function(err, length) {
    if(err) cb(err);

    log.verbose(length, "form.getLength()");

    headers["content-length"] = length;

    // Set the headers on the request object
    r.setHeaders(headers);
  });

  r.on("error", function (er) {
    log.er(er);
    cb(er);
  })

  r.on('response', function(response){

    log.silly('received response');
    response.setEncoding('utf8');

    var data = '';
    response.on('data', function(chunk){
      data = data + chunk;
    });

    response.on('end', function(){
      log.silly(data, "DATA");
      data = JSON.parse(data);
      return cb(undefined, data);
    });

    response.on('error', function(err) {
      return cb(err);
    });
  });

  return r;
}

function File (name, cb) {
  var f = this;
  f.name = name;
  if (f.loaded) return cb(undefined, f);
  log.info(f.name, "stat");
  fs.stat(f.name, function (er, stat) {
    if (er) return log.er(cb, "doesn't exist "+f.name)(er);
    log.silly(stat, "stat "+name);
    f.length = stat.size;
    fs.open(f.name, "r", 0666, function (er, fd) {
      if (er) return log.er(cb, "Error opening "+f.name)(er);
      f.fd = fd;
      cb(undefined, f);
    });
  });
};

function getFeedHenryUrl () {
  var r = fhc.config.get("feedhenry");
  log.verbose(r, "feedhenry url");
  if (!r) {
    return new Error("Must define feedhenry URL before accessing FeedHenry.");
  }
  if (r.substr(-1) !== "/") r += "/";
  fhc.config.set("feedhenry", r);
  return r;
};

function getMessagingUrl () {
  var r = fhc.config.get("messaging");
  if (!r) {
    return new Error("Must define messaging URL before accessing FeedHenry Message Server.");
  }
  if (r.substr(-1) !== "/") r += "/";
  fhc.config.set("messaging", r);
  return r;
}

function resolveUrl(where, fhurl) {
  log.verbose(where, "resolveUrl - IN");
  if (!where.match(/^https?:\/\//)) {
    if (where.charAt(0) !== "/") where = "/" + where;
    fhurl = fhurl || getFeedHenryUrl();
    where = url.resolve(fhurl, where);
  }

  log.verbose(where, "resolveUrl - OUT");

  return where;
}

function getHeaders() {
  var headers = { "accept" : "application/json" };
  var cookie = fhc.config.get("cookie");
  if (cookie != undefined) {
    headers.cookie = "feedhenry=" + cookie + ";";
    log.silly(headers.cookie, "cookie");
  }

  log.silly(fhc.config.get('nocache'), "nocache");
  if (fhc.config.get('nocache')) {
    headers['X-FeedHenry-CacheOff'] = "script_super";
  }
  try {
    var explainLvl = parseInt(fhc.config.get('explain'), 10);
    log.silly(explainLvl, "explain level");
    if (explainLvl > 0) {
      headers['X-FeedHenry-Explain'] = 'active' + ((explainLvl > 1) ? ' stacktrace' : '');
    }
  } catch (e) {
    log.error('explain config value should be a Number: ' + e);
  }
  var platform = '<node 0.4>';
  var release = '<node 0.4>';
  try{
    platform = os.platform();
    release = os.release();
  } catch (x) {
    // ignored, this will fail on node 0.4.x
  }
  headers['User-Agent'] = "FHC/" + fhc.version + ' ' + platform + '/' + release;

  var apiKey = keys.getUserApiKey();
  if(null != apiKey && (typeof apiKey !== "undefined")){
    headers['X-FH-AUTH-USER'] = apiKey;
  }

  log.verbose(headers, "getHeaders");
  return headers;
}

function getProxy(url) {
  var proxy
  if (url.protocol !== "https:" || !(proxy = fhc.config.get("https-proxy"))) {
    proxy = fhc.config.get("proxy")
  }
  log.silly(proxy, "getProxy");
  return proxy;
}<|endoftext|>


<commit_before>/**
 * @namespace VENT.renderer
 */

VENT.renderer = (function() {

    var _list, _len, _interval;

    _list = [];
    _len = 0;
    _interval = 33;

    function _loop() {
        for (var i = 0; i < _len; i++) { _list[i](); }
        setTimeout(_loop, _interval);
    }

    function _registerRender(fn) {
        var len = _list.push(fn);
        _len = len;
        return len - 1;
    }

    function _deregisterRender(i) {
        _list.splice(i, 1);
        _len = _list.length;
    }

    function _setFPS(fps) {
        _interval = Math.floor(1000 / fps);
    }

    _loop();

    return {
        registerRender: _registerRender,
        deregisterRender: _deregisterRender,
        setFPS: _setFPS
    }

}());
<commit_msg>Switched from Timeout render loop to a requestAnimationFrame, with polyfill.<commit_after>/**
 * @namespace VENT.renderer
 */

VENT.renderer = (function() {

    // requestAnimationFrame Shim
    window.requestAnimFrame = (function requestAnimFrame() {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {
                window.setTimeout(callback, 1000 / 60);
            }
    })();

    var _list, _len, _interval;

    _list = [];
    _len = 0;
    _interval = 33;

    function _loop() {
        for (var i = 0; i < _len; i++) { _list[i](); }
        requestAnimFrame(_loop);
    }

    function _registerRender(fn) {
        var len = _list.push(fn);
        _len = len;
        return len - 1;
    }

    function _deregisterRender(i) {
        _list.splice(i, 1);
        _len = _list.length;
    }

    function _setFPS(fps) {
        _interval = Math.floor(1000 / fps);
    }

    _loop();

    return {
        registerRender: _registerRender,
        deregisterRender: _deregisterRender,
        setFPS: _setFPS
    }

}());
<|endoftext|>


<commit_before>var blessed = require('blessed');

module.exports = function(screen, filters) {
  var view = new blessed.List({
    name: 'filters',
    parent: screen,
    width: '30%',
    height: '20%',
    top: 'center',
    left: 'center',
    tags: true,
    bg: 'lightblack',
    selectedFg: 'black',
    selectedBg: 'yellow',
    keys: true,
    vi: true,
    border: {
      type: 'line'
    }
  });

  view.key(['escape', 'h', 'enter'], function() {
    screen.remove(view);
    screen.render();
  });

  view.key(['delete', 'enter', 'x'], function(text, i) {
    view.removeItem(view.selected);
    filters.remove(view.selected);
    screen.render();
  });

  view.setItems(filters.serialize().map(function(filter) {
    return filter.type + ' = ' + filter.value;
  }));

  view.select(0);
  view.focus();

  screen.render();
};
<commit_msg>fix off by one error by removing the item from the model before the index on the view changes<commit_after>var blessed = require('blessed');

module.exports = function(screen, filters) {
  var view = new blessed.List({
    name: 'filters',
    parent: screen,
    width: '30%',
    height: '20%',
    top: 'center',
    left: 'center',
    tags: true,
    bg: 'lightblack',
    selectedFg: 'black',
    selectedBg: 'yellow',
    keys: true,
    vi: true,
    border: {
      type: 'line'
    }
  });

  view.key(['escape', 'h', 'enter'], function() {
    screen.remove(view);
    screen.render();
  });

  view.key(['delete', 'enter', 'x'], function(text, i) {
    filters.remove(view.selected);
    view.removeItem(view.selected);
    screen.render();
  });

  view.setItems(filters.serialize().map(function(filter) {
    return filter.type + ' = ' + filter.value;
  }));

  view.select(0);
  view.focus();

  screen.render();
};
<|endoftext|>