<commit_before>'use strict';
var Promise = require('bluebird');
var auth = Promise.promisify(require('google-auth2-service-account').auth);
var scope = 'https://www.googleapis.com/auth/mapsengine';
var rawRequest = Promise.promisify(require('request'));
var Deque = require('double-ended-queue');
var fs = require('fs');
var Bulk = require('./bulk');
var limits = require('./limits.json');
var Stream = require('./stream');
var FeatureStream = require('./featureStream');
var readFile = Promise.promisify(fs.readFile, fs);
var TRUE = Promise.resolve(true);
module.exports = GME;
function GME(key, email, table, opts) {
  if (Buffer.isBuffer(key)) {
    this.key = Promise.resolve(key);
  } else {
    this.key = readFile(key);
  }
  if (typeof opts === 'string') {
    opts = {
      primary: opts
    };
  }
  this.iss = email;
  this.tableid = table;
  opts = opts || {};
  this.primary = opts.primary || 'gx_id';
  this.verbose = !!opts.verbose;
  this.nogeo = !!opts.nogeo;
  this.stopOnError = !!opts.stopOnError;
  if (opts.trace) {
    this.trace = opts.trace;
  } else {
    this.trace = false;
  }
  this.baseurl = 'https://www.googleapis.com/mapsengine/v1/';
  this.queue = new Deque();
  this.inProgress = false;
  if (opts.limits && typeof opts.limits === 'object') {
    this.limits = opts.limits;
  } else if (typeof opts.limits === 'string') {
    if (limits[opts.limits]) {
      this.limits = limits[opts.limits];
    }
  }
}

GME.prototype._request = function (opts) {
  return rawRequest(opts).then(function (resp) {
    resp = resp[1];
    if (resp && resp.error) {
      throw resp.error;
    }
    return resp;
  });
};
GME.prototype.request = function (opts) {
  if (this.inProgress) {
    var resolver = Promise.defer();
    this.queue.push({
      resolver: resolver,
      opts: opts
    });
    return resolver.promise;
  }
  return this.createRequest(opts);
};
GME.prototype.createRequest = function (opts) {
  var self = this;
  this.inProgress = true;
  var tries = 1;
  function attemptDownload(){
    return self.auth().then(function (auth) {
      opts.headers = {
        Authorization: 'Bearer ' + auth
      };
      return self._request(opts);
    }).catch(function (err) {
      if (tries++ > 10 || [401, 403, 429].indexOf(err.code) === -1 || self.stopOnError) {
        var error = new Error(err.message);
        error.code = err.code;
        error.errors = err.errors;
        if (self.verbose) {
          if (self.nogeo) {
            try {
              opts.body.features = opts.body.features.map(function (feature) {
                delete feature.geometry;
                return feature;
              });
            } catch(e) {}
          }
          console.log(new Date());
          console.log(err);
          console.log(JSON.stringify(opts, false, 4));
        }
        throw error;
      }
      return Promise.delay(tries * 1000).then(function () {
        return attemptDownload();
      });
    });
  }
  return attemptDownload().finally(function () {
    if (self.queue.length) {
      var next = self.queue.shift();
      next.resolver.resolve(self.createRequest(next.opts));
      return;
    }
    self.inProgress = false;
  });
};

GME.prototype.init = function () {
  var self = this;
  return this.key.then(function (key) {
    return auth(key, {
      iss: self.iss,
      scope: scope
    }).then(function (token) {
      if (self.timeout) {
        clearTimeout(self.timeout);
        self.timeout = null;
      }
      self.token = token;
      self.timeout = setTimeout(function () {
        self.token = null;
        self.timeout = null;
      }, 5 * 60 * 1000);
      return token;
    });
  });
};
GME.prototype.auth = function () {
  if (this.token) {
    return Promise.resolve(this.token);
  } else {
    return this.init();
  }
};
GME.prototype.get = function (url, qs) {
  qs = qs || {};
  url = this.baseurl + url;
  var self = this;
  if (this.trace) {
    qs.trace = this.trace;
  }

  return self.request({
    url: url,
    qs: qs,
    json: true
  });
};
GME.prototype.post = function (url, body) {
  url = this.baseurl + url;
  var self = this;
  var opts = {
    url: url,

    body: body,
    json: true,
    method: 'POST'
  };
  if (self.trace) {
    opts.qs = {
      trace: self.trace
    };
  }
  return self.request(opts);
};
GME.prototype.info = function () {
  return this.get('tables/' + this.tableid);
};
GME.prototype.features = function (query) {
  var self = this;
  return new Promise(function (resolve, reject) {
    var out = {
      type: 'FeatureCollection',
      features: []
    };
    self.readStream(query).on('data', function (d) {
      out.features.push(d);
    }).on('finish', function () {
      resolve(out);
    }).on('error', reject);
  });
};
GME.prototype.featureStream = GME.prototype.readStream = function (query) {
  query = query || {};
  return new FeatureStream(this, query);
};
GME.prototype.feature = function (id) {
  return this.get('tables/' + this.tableid + '/features/' + id);
};
GME.prototype.create = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('create', arr.length);
    arr.forEach(function (item) {
      console.log(item.properties[this.primary]);
    }, this);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchInsert', {features: arr});
};
GME.prototype.update = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('update', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchPatch', {features: arr});
};
GME.prototype.remove = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('remove', arr.length);
    arr.forEach(function (item) {
      console.log(item);
    }, this);
    console.log(this.tableid);
  }
  if (!arr.length) {
    return TRUE;
  }
  var self = this;
  if (this.primary !== 'gx_id') {
    return Promise.all(arr.map(this._fetchPrimaryKey, this)).then(function (arr) {
      return self.post('tables/' + self.tableid + '/features/batchDelete', {primaryKeys: arr});
    });
  } else {
    return self.post('tables/' + self.tableid + '/features/batchDelete', {primaryKeys: arr});
  }
};
GME.prototype._fetchPrimaryKey = function (item) {
  var query = {where: this.primary + '=\'' + item + '\''};
  return this.features(query).then(function (resp) {
    if (!resp.features.length) {
      return item;
    }
    console.log(item, resp.features[0].properties.gx_id);
    return resp.features[0].properties.gx_id || item;
  });
};

GME.prototype.bulk = function() {
  return new Bulk(this);
};
GME.prototype.writeStream = function() {
  return new Stream(this);
};
<commit_msg>clean up console.log<commit_after>'use strict';
var Promise = require('bluebird');
var auth = Promise.promisify(require('google-auth2-service-account').auth);
var scope = 'https://www.googleapis.com/auth/mapsengine';
var rawRequest = Promise.promisify(require('request'));
var Deque = require('double-ended-queue');
var fs = require('fs');
var Bulk = require('./bulk');
var limits = require('./limits.json');
var Stream = require('./stream');
var FeatureStream = require('./featureStream');
var readFile = Promise.promisify(fs.readFile, fs);
var TRUE = Promise.resolve(true);
module.exports = GME;
function GME(key, email, table, opts) {
  if (Buffer.isBuffer(key)) {
    this.key = Promise.resolve(key);
  } else {
    this.key = readFile(key);
  }
  if (typeof opts === 'string') {
    opts = {
      primary: opts
    };
  }
  this.iss = email;
  this.tableid = table;
  opts = opts || {};
  this.primary = opts.primary || 'gx_id';
  this.verbose = !!opts.verbose;
  this.nogeo = !!opts.nogeo;
  this.stopOnError = !!opts.stopOnError;
  if (opts.trace) {
    this.trace = opts.trace;
  } else {
    this.trace = false;
  }
  this.baseurl = 'https://www.googleapis.com/mapsengine/v1/';
  this.queue = new Deque();
  this.inProgress = false;
  if (opts.limits && typeof opts.limits === 'object') {
    this.limits = opts.limits;
  } else if (typeof opts.limits === 'string') {
    if (limits[opts.limits]) {
      this.limits = limits[opts.limits];
    }
  }
}

GME.prototype._request = function (opts) {
  return rawRequest(opts).then(function (resp) {
    resp = resp[1];
    if (resp && resp.error) {
      throw resp.error;
    }
    return resp;
  });
};
GME.prototype.request = function (opts) {
  if (this.inProgress) {
    var resolver = Promise.defer();
    this.queue.push({
      resolver: resolver,
      opts: opts
    });
    return resolver.promise;
  }
  return this.createRequest(opts);
};
GME.prototype.createRequest = function (opts) {
  var self = this;
  this.inProgress = true;
  var tries = 1;
  function attemptDownload(){
    return self.auth().then(function (auth) {
      opts.headers = {
        Authorization: 'Bearer ' + auth
      };
      return self._request(opts);
    }).catch(function (err) {
      if (tries++ > 10 || [401, 403, 429].indexOf(err.code) === -1 || self.stopOnError) {
        var error = new Error(err.message);
        error.code = err.code;
        error.errors = err.errors;
        if (self.verbose) {
          if (self.nogeo) {
            try {
              opts.body.features = opts.body.features.map(function (feature) {
                delete feature.geometry;
                return feature;
              });
            } catch(e) {}
          }
          console.log(new Date());
          console.log(err);
          console.log(JSON.stringify(opts, false, 4));
        }
        throw error;
      }
      return Promise.delay(tries * 1000).then(function () {
        return attemptDownload();
      });
    });
  }
  return attemptDownload().finally(function () {
    if (self.queue.length) {
      var next = self.queue.shift();
      next.resolver.resolve(self.createRequest(next.opts));
      return;
    }
    self.inProgress = false;
  });
};

GME.prototype.init = function () {
  var self = this;
  return this.key.then(function (key) {
    return auth(key, {
      iss: self.iss,
      scope: scope
    }).then(function (token) {
      if (self.timeout) {
        clearTimeout(self.timeout);
        self.timeout = null;
      }
      self.token = token;
      self.timeout = setTimeout(function () {
        self.token = null;
        self.timeout = null;
      }, 5 * 60 * 1000);
      return token;
    });
  });
};
GME.prototype.auth = function () {
  if (this.token) {
    return Promise.resolve(this.token);
  } else {
    return this.init();
  }
};
GME.prototype.get = function (url, qs) {
  qs = qs || {};
  url = this.baseurl + url;
  var self = this;
  if (this.trace) {
    qs.trace = this.trace;
  }

  return self.request({
    url: url,
    qs: qs,
    json: true
  });
};
GME.prototype.post = function (url, body) {
  url = this.baseurl + url;
  var self = this;
  var opts = {
    url: url,

    body: body,
    json: true,
    method: 'POST'
  };
  if (self.trace) {
    opts.qs = {
      trace: self.trace
    };
  }
  return self.request(opts);
};
GME.prototype.info = function () {
  return this.get('tables/' + this.tableid);
};
GME.prototype.features = function (query) {
  var self = this;
  return new Promise(function (resolve, reject) {
    var out = {
      type: 'FeatureCollection',
      features: []
    };
    self.readStream(query).on('data', function (d) {
      out.features.push(d);
    }).on('finish', function () {
      resolve(out);
    }).on('error', reject);
  });
};
GME.prototype.featureStream = GME.prototype.readStream = function (query) {
  query = query || {};
  return new FeatureStream(this, query);
};
GME.prototype.feature = function (id) {
  return this.get('tables/' + this.tableid + '/features/' + id);
};
GME.prototype.create = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('create', arr.length);
    arr.forEach(function (item) {
      console.log(item.properties[this.primary]);
    }, this);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchInsert', {features: arr});
};
GME.prototype.update = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('update', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchPatch', {features: arr});
};
GME.prototype.remove = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('remove', arr.length);
    arr.forEach(function (item) {
      console.log(item);
    }, this);
    console.log(this.tableid);
  }
  if (!arr.length) {
    return TRUE;
  }
  var self = this;
  if (this.primary !== 'gx_id') {
    return Promise.all(arr.map(this._fetchPrimaryKey, this)).then(function (arr) {
      return self.post('tables/' + self.tableid + '/features/batchDelete', {primaryKeys: arr});
    });
  } else {
    return self.post('tables/' + self.tableid + '/features/batchDelete', {primaryKeys: arr});
  }
};
GME.prototype._fetchPrimaryKey = function (item) {
  var query = {where: this.primary + '=\'' + item + '\''};
  var self = this;
  return this.features(query).then(function (resp) {
    if (!resp.features.length) {
      return item;
    }
    var out = resp.features[0].properties.gx_id || item;
    if (self.verbose) {
      console.log(item, out);
    }
    return out;
  });
};

GME.prototype.bulk = function() {
  return new Bulk(this);
};
GME.prototype.writeStream = function() {
  return new Stream(this);
};
<|endoftext|>


<commit_before>/* @flow */

import { Disposable, Point } from "atom";
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import os from "os";
import path from "path";

import Config from "./config";
import store from "./store";

export const INSPECTOR_URI = "atom://hydrogen/inspector";
export const WATCHES_URI = "atom://hydrogen/watch-sidebar";
export const OUTPUT_AREA_URI = "atom://hydrogen/output-area";
export const KERNEL_MONITOR_URI = "atom://hydrogen/kernel-monitor";

export function reactFactory(
  reactElement: React$Element<any>,
  domElement: HTMLElement,
  additionalTeardown: ?Function,
  disposer: atom$CompositeDisposable = store.subscriptions
) {
  ReactDOM.render(reactElement, domElement);

  const disposable = new Disposable(() => {
    ReactDOM.unmountComponentAtNode(domElement);
    if (typeof additionalTeardown === "function") additionalTeardown();
  });

  disposer.add(disposable);
}

export function focus(item: ?mixed) {
  if (item) {
    const editorPane = atom.workspace.paneForItem(item);
    if (editorPane) editorPane.activate();
  }
}

export async function openOrShowDock(URI: string): Promise<?void> {
  // atom.workspace.open(URI) will activate/focus the dock by default
  // dock.toggle() or dock.show() will leave focus wherever it was

  // this function is basically workspace.open, except it
  // will not focus the newly opened pane
  let dock = atom.workspace.paneContainerForURI(URI);
  if (dock) {
    // If the target item already exist, activate it and show dock
    const pane = atom.workspace.paneForURI(URI);
    if (pane) pane.activateItemForURI(URI);
    return dock.show();
  }

  await atom.workspace.open(URI, {
    searchAllPanes: true,
    activatePane: false
  });
  dock = atom.workspace.paneContainerForURI(URI);
  return dock ? dock.show() : null;
}

export function grammarToLanguage(grammar: ?atom$Grammar) {
  if (!grammar) return null;
  const grammarLanguage = grammar.name.toLowerCase();

  const mappings = Config.getJson("languageMappings");
  const kernelLanguage = _.findKey(
    mappings,
    l => l.toLowerCase() === grammarLanguage
  );

  return kernelLanguage ? kernelLanguage.toLowerCase() : grammarLanguage;
}

/**
 * Copied from https://github.com/nteract/nteract/blob/master/src/notebook/epics/execute.js#L37
 * Create an object that adheres to the jupyter notebook specification.
 * http://jupyter-client.readthedocs.io/en/latest/messaging.html
 *
 * @param {Object} msg - Message that has content which can be converted to nbformat
 * @return {Object} formattedMsg  - Message with the associated output type
 */
export function msgSpecToNotebookFormat(message: Message) {
  return Object.assign({}, message.content, {
    output_type: message.header.msg_type
  });
}

/**
 * A very basic converter for supporting jupyter messaging protocol v4 replies
 */
export function msgSpecV4toV5(message: Message) {
  switch (message.header.msg_type) {
    case "pyout":
      message.header.msg_type = "execute_result";
      break;
    case "pyerr":
      message.header.msg_type = "error";
      break;
    case "stream":
      if (!message.content.text) message.content.text = message.content.data;
      break;
  }
  return message;
}

const markupGrammars = new Set([
  "source.gfm",
  "source.asciidoc",
  "text.restructuredtext",
  "text.tex.latex.knitr",
  "text.md",
  "source.weave.noweb",
  "source.weave.md",
  "source.weave.latex",
  "source.weave.restructuredtext",
  "source.pweave.noweb",
  "source.pweave.md",
  "source.pweave.latex",
  "source.pweave.restructuredtext",
  "source.dyndoc.md.stata",
  "source.dyndoc.latex.stata"
]);

export function isMultilanguageGrammar(grammar: atom$Grammar) {
  return markupGrammars.has(grammar.scopeName);
}

export function deletedFilePath(filePath: string): string {
  return filePath + " (Destroyed)";
}
// @TODO If our store holds editor IDs instead of file paths, these messy matching stuff below would
//       easily be replaced by simpler code. See also components/kernel-monitor.js for this problem.
export const isUnsavedFilePath = (filePath: string): boolean => {
  return filePath.match(/Unsaved\sEditor\s\d+/) ? true : false;
};
export const isDeletedFilePath = (filePath: string): boolean => {
  return filePath.match(/^.*\s\(Destroyed\)$/) ? true : false;
};

export function kernelSpecProvidesGrammar(
  kernelSpec: Kernelspec,
  grammar: ?atom$Grammar
) {
  if (!grammar || !grammar.name || !kernelSpec || !kernelSpec.language) {
    return false;
  }
  const grammarLanguage = grammar.name.toLowerCase();
  const kernelLanguage = kernelSpec.language.toLowerCase();
  if (kernelLanguage === grammarLanguage) {
    return true;
  }

  const mappedLanguage = Config.getJson("languageMappings")[kernelLanguage];
  if (!mappedLanguage) {
    return false;
  }

  return mappedLanguage.toLowerCase() === grammarLanguage;
}

export function getEmbeddedScope(
  editor: atom$TextEditor,
  position: atom$Point
): ?string {
  const scopes = editor
    .scopeDescriptorForBufferPosition(position)
    .getScopesArray();
  return _.find(scopes, s => s.indexOf("source.embedded.") === 0);
}

export function getEditorDirectory(editor: ?atom$TextEditor) {
  if (!editor) return os.homedir();
  const editorPath = editor.getPath();
  return editorPath ? path.dirname(editorPath) : os.homedir();
}

export function log(...message: Array<any>) {
  if (atom.config.get("Hydrogen.debug")) {
    console.debug("Hydrogen:", ...message);
  }
}

export function renderDevTools(enableLogging: boolean) {
  if (!atom.devMode) return;
  try {
    const devTools = require("mobx-react-devtools");
    const div = document.createElement("div");
    document.getElementsByTagName("body")[0].appendChild(div);
    devTools.setLogEnabled(enableLogging);
    ReactDOM.render(<devTools.default noPanel />, div);
  } catch (e) {
    log("Could not enable dev tools", e);
  }
}

export function hotReloadPackage() {
  const packName = "Hydrogen";
  const packPath = atom.packages.resolvePackagePath(packName);
  if (!packPath) return;
  const packPathPrefix = packPath + path.sep;
  const zeromqPathPrefix =
    path.join(packPath, "node_modules", "zeromq") + path.sep;

  console.info(`deactivating ${packName}`);
  atom.packages.deactivatePackage(packName);
  atom.packages.unloadPackage(packName);

  // Delete require cache to re-require on activation.
  // But except zeromq native module which is not re-requireable.
  const packageLibsExceptZeromq = filePath =>
    filePath.startsWith(packPathPrefix) &&
    !filePath.startsWith(zeromqPathPrefix);

  Object.keys(require.cache)
    .filter(packageLibsExceptZeromq)
    .forEach(filePath => delete require.cache[filePath]);

  atom.packages.loadPackage(packName);
  atom.packages.activatePackage(packName);
  console.info(`activated ${packName}`);
}

export function rowRangeForCodeFoldAtBufferRow(
  editor: atom$TextEditor,
  row: number
) {
  if (parseFloat(atom.getVersion()) < 1.22) {
    return editor.languageMode.rowRangeForCodeFoldAtBufferRow(row);
  } else {
    // $FlowFixMe
    const range = editor.tokenizedBuffer.getFoldableRangeContainingPoint(
      new Point(row, Infinity),
      editor.getTabLength()
    );

    return range ? [range.start.row, range.end.row] : null;
  }
}

export const EmptyMessage = () => {
  return (

No output to display

  );
};

/**
 * Given a message whose type if `execute_reply`, calculates exection time and returns its string representation.
 *
 * @param {Message} message - A Message object whose type is `execute_reply`
 * @return {String} - A string representation of the execution time
 */
export function executionTime(message: Message): string {
  if (!message.parent_header.date || !message.header.date) {
    return "Not available";
  }
  const start = Date.parse(message.parent_header.date);
  const end = Date.parse(message.header.date);
  let time = end - start; // milliseconds
  let sec = time / 1000; // seconds
  if (sec < 60) {
    return sec.toFixed(3) + " sec";
  }
  let min = (sec - (sec % 60)) / 60;
  sec = (sec % 60).toFixed(0);
  if (min < 60) {
    return min + " min " + sec + " sec";
  }
  let hour = (min - (min % 60)) / 60;
  min %= 60;
  return hour + " h " + min + " m " + sec + " s";
}

export function js_idx_to_char_idx(js_idx: number, text: string | null) {
  if (text === null) return -1;
  var char_idx = js_idx;
  for (var i = 0; i < text.length && i < js_idx; i++) {
    var char_code = text.charCodeAt(i);
    // check for the first half of a surrogate pair
    if (char_code >= 0xd800 && char_code < 0xdc00) {
      char_idx -= 1;
    }
  }
  return char_idx;
}

export function char_idx_to_js_idx(char_idx: number, text: string | null) {
  if (text === null) return -1;
  var js_idx = char_idx;
  for (var i = 0; i < text.length && i < js_idx; i++) {
    var char_code = text.charCodeAt(i);
    // check for the first half of a surrogate pair
    if (char_code >= 0xd800 && char_code < 0xdc00) {
      js_idx += 1;
    }
  }
  return js_idx;
}
<commit_msg>`Destroyed` -> `Deleted`<commit_after>/* @flow */

import { Disposable, Point } from "atom";
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import os from "os";
import path from "path";

import Config from "./config";
import store from "./store";

export const INSPECTOR_URI = "atom://hydrogen/inspector";
export const WATCHES_URI = "atom://hydrogen/watch-sidebar";
export const OUTPUT_AREA_URI = "atom://hydrogen/output-area";
export const KERNEL_MONITOR_URI = "atom://hydrogen/kernel-monitor";

export function reactFactory(
  reactElement: React$Element<any>,
  domElement: HTMLElement,
  additionalTeardown: ?Function,
  disposer: atom$CompositeDisposable = store.subscriptions
) {
  ReactDOM.render(reactElement, domElement);

  const disposable = new Disposable(() => {
    ReactDOM.unmountComponentAtNode(domElement);
    if (typeof additionalTeardown === "function") additionalTeardown();
  });

  disposer.add(disposable);
}

export function focus(item: ?mixed) {
  if (item) {
    const editorPane = atom.workspace.paneForItem(item);
    if (editorPane) editorPane.activate();
  }
}

export async function openOrShowDock(URI: string): Promise<?void> {
  // atom.workspace.open(URI) will activate/focus the dock by default
  // dock.toggle() or dock.show() will leave focus wherever it was

  // this function is basically workspace.open, except it
  // will not focus the newly opened pane
  let dock = atom.workspace.paneContainerForURI(URI);
  if (dock) {
    // If the target item already exist, activate it and show dock
    const pane = atom.workspace.paneForURI(URI);
    if (pane) pane.activateItemForURI(URI);
    return dock.show();
  }

  await atom.workspace.open(URI, {
    searchAllPanes: true,
    activatePane: false
  });
  dock = atom.workspace.paneContainerForURI(URI);
  return dock ? dock.show() : null;
}

export function grammarToLanguage(grammar: ?atom$Grammar) {
  if (!grammar) return null;
  const grammarLanguage = grammar.name.toLowerCase();

  const mappings = Config.getJson("languageMappings");
  const kernelLanguage = _.findKey(
    mappings,
    l => l.toLowerCase() === grammarLanguage
  );

  return kernelLanguage ? kernelLanguage.toLowerCase() : grammarLanguage;
}

/**
 * Copied from https://github.com/nteract/nteract/blob/master/src/notebook/epics/execute.js#L37
 * Create an object that adheres to the jupyter notebook specification.
 * http://jupyter-client.readthedocs.io/en/latest/messaging.html
 *
 * @param {Object} msg - Message that has content which can be converted to nbformat
 * @return {Object} formattedMsg  - Message with the associated output type
 */
export function msgSpecToNotebookFormat(message: Message) {
  return Object.assign({}, message.content, {
    output_type: message.header.msg_type
  });
}

/**
 * A very basic converter for supporting jupyter messaging protocol v4 replies
 */
export function msgSpecV4toV5(message: Message) {
  switch (message.header.msg_type) {
    case "pyout":
      message.header.msg_type = "execute_result";
      break;
    case "pyerr":
      message.header.msg_type = "error";
      break;
    case "stream":
      if (!message.content.text) message.content.text = message.content.data;
      break;
  }
  return message;
}

const markupGrammars = new Set([
  "source.gfm",
  "source.asciidoc",
  "text.restructuredtext",
  "text.tex.latex.knitr",
  "text.md",
  "source.weave.noweb",
  "source.weave.md",
  "source.weave.latex",
  "source.weave.restructuredtext",
  "source.pweave.noweb",
  "source.pweave.md",
  "source.pweave.latex",
  "source.pweave.restructuredtext",
  "source.dyndoc.md.stata",
  "source.dyndoc.latex.stata"
]);

export function isMultilanguageGrammar(grammar: atom$Grammar) {
  return markupGrammars.has(grammar.scopeName);
}

export function deletedFilePath(filePath: string): string {
  return filePath + " (Deleted)";
}
// @TODO If our store holds editor IDs instead of file paths, these messy matching stuff below would
//       easily be replaced by simpler code. See also components/kernel-monitor.js for this problem.
export const isUnsavedFilePath = (filePath: string): boolean => {
  return filePath.match(/Unsaved\sEditor\s\d+/) ? true : false;
};
export const isDeletedFilePath = (filePath: string): boolean => {
  return filePath.match(/^.*\s\(Deleted\)$/) ? true : false;
};

export function kernelSpecProvidesGrammar(
  kernelSpec: Kernelspec,
  grammar: ?atom$Grammar
) {
  if (!grammar || !grammar.name || !kernelSpec || !kernelSpec.language) {
    return false;
  }
  const grammarLanguage = grammar.name.toLowerCase();
  const kernelLanguage = kernelSpec.language.toLowerCase();
  if (kernelLanguage === grammarLanguage) {
    return true;
  }

  const mappedLanguage = Config.getJson("languageMappings")[kernelLanguage];
  if (!mappedLanguage) {
    return false;
  }

  return mappedLanguage.toLowerCase() === grammarLanguage;
}

export function getEmbeddedScope(
  editor: atom$TextEditor,
  position: atom$Point
): ?string {
  const scopes = editor
    .scopeDescriptorForBufferPosition(position)
    .getScopesArray();
  return _.find(scopes, s => s.indexOf("source.embedded.") === 0);
}

export function getEditorDirectory(editor: ?atom$TextEditor) {
  if (!editor) return os.homedir();
  const editorPath = editor.getPath();
  return editorPath ? path.dirname(editorPath) : os.homedir();
}

export function log(...message: Array<any>) {
  if (atom.config.get("Hydrogen.debug")) {
    console.debug("Hydrogen:", ...message);
  }
}

export function renderDevTools(enableLogging: boolean) {
  if (!atom.devMode) return;
  try {
    const devTools = require("mobx-react-devtools");
    const div = document.createElement("div");
    document.getElementsByTagName("body")[0].appendChild(div);
    devTools.setLogEnabled(enableLogging);
    ReactDOM.render(<devTools.default noPanel />, div);
  } catch (e) {
    log("Could not enable dev tools", e);
  }
}

export function hotReloadPackage() {
  const packName = "Hydrogen";
  const packPath = atom.packages.resolvePackagePath(packName);
  if (!packPath) return;
  const packPathPrefix = packPath + path.sep;
  const zeromqPathPrefix =
    path.join(packPath, "node_modules", "zeromq") + path.sep;

  console.info(`deactivating ${packName}`);
  atom.packages.deactivatePackage(packName);
  atom.packages.unloadPackage(packName);

  // Delete require cache to re-require on activation.
  // But except zeromq native module which is not re-requireable.
  const packageLibsExceptZeromq = filePath =>
    filePath.startsWith(packPathPrefix) &&
    !filePath.startsWith(zeromqPathPrefix);

  Object.keys(require.cache)
    .filter(packageLibsExceptZeromq)
    .forEach(filePath => delete require.cache[filePath]);

  atom.packages.loadPackage(packName);
  atom.packages.activatePackage(packName);
  console.info(`activated ${packName}`);
}

export function rowRangeForCodeFoldAtBufferRow(
  editor: atom$TextEditor,
  row: number
) {
  if (parseFloat(atom.getVersion()) < 1.22) {
    return editor.languageMode.rowRangeForCodeFoldAtBufferRow(row);
  } else {
    // $FlowFixMe
    const range = editor.tokenizedBuffer.getFoldableRangeContainingPoint(
      new Point(row, Infinity),
      editor.getTabLength()
    );

    return range ? [range.start.row, range.end.row] : null;
  }
}

export const EmptyMessage = () => {
  return (

No output to display

  );
};

/**
 * Given a message whose type if `execute_reply`, calculates exection time and returns its string representation.
 *
 * @param {Message} message - A Message object whose type is `execute_reply`
 * @return {String} - A string representation of the execution time
 */
export function executionTime(message: Message): string {
  if (!message.parent_header.date || !message.header.date) {
    return "Not available";
  }
  const start = Date.parse(message.parent_header.date);
  const end = Date.parse(message.header.date);
  let time = end - start; // milliseconds
  let sec = time / 1000; // seconds
  if (sec < 60) {
    return sec.toFixed(3) + " sec";
  }
  let min = (sec - (sec % 60)) / 60;
  sec = (sec % 60).toFixed(0);
  if (min < 60) {
    return min + " min " + sec + " sec";
  }
  let hour = (min - (min % 60)) / 60;
  min %= 60;
  return hour + " h " + min + " m " + sec + " s";
}

export function js_idx_to_char_idx(js_idx: number, text: string | null) {
  if (text === null) return -1;
  var char_idx = js_idx;
  for (var i = 0; i < text.length && i < js_idx; i++) {
    var char_code = text.charCodeAt(i);
    // check for the first half of a surrogate pair
    if (char_code >= 0xd800 && char_code < 0xdc00) {
      char_idx -= 1;
    }
  }
  return char_idx;
}

export function char_idx_to_js_idx(char_idx: number, text: string | null) {
  if (text === null) return -1;
  var js_idx = char_idx;
  for (var i = 0; i < text.length && i < js_idx; i++) {
    var char_code = text.charCodeAt(i);
    // check for the first half of a surrogate pair
    if (char_code >= 0xd800 && char_code < 0xdc00) {
      js_idx += 1;
    }
  }
  return js_idx;
}
<|endoftext|>


<commit_before>'use strict';

const utils = require('./utils');
const postProcessing = require('./post-processing');

// utility used to plot a value in a nested array
function plotInMatrix (matrix, coords, val, endDepth) {
	let i = coords.shift();
  let toModify = matrix;
	while (coords.length - endDepth) {
    toModify[i] = toModify[i] || [];
		toModify = toModify[i];
		i = coords.shift();
  }
	toModify[i] = val;
}

// converts flat data structure e.g. { '0,2,3': 'value'} into a matrix of nested arrays
// positive endDepth means it will leave the lower levels of the matrix flattened once n levels have been nested
function toNested (data, endDepth) {
	const matrix = [];
	Object.keys(data).forEach(coords => {
		plotInMatrix(matrix, coords.split(','), data[coords], endDepth || 0);
	});
	return matrix;
}

// converts flat data structure e.g. { '0,2,3': 'value'} into a matrix of nested arrays
// similar to the above, but starts from the leaves rather than the root of the matrix
// the above function, when endLevel is defined, will return nested arrays which contain flat data structures as their leaves
// this function, on the other hand, will return a flat data structure with nested arrays as the leaves
function nestLowerLevels (flatData, endLevel) {
	const obj = {};
	Object.keys(flatData).forEach(k => {
		const coords = k.split(',');
		const bucketCoord = coords.slice(0, endLevel);
		obj[bucketCoord] = obj[bucketCoord] || {};
		obj[bucketCoord][coords.slice(endLevel)] = flatData[k];
	})
	Object.keys(obj).forEach(k => {
		obj[k] = toNested(obj[k]);
	})
	return obj
}

class Table {
	constructor (conf) {
		this.name = conf.name;
		this.data = conf.data;
		this.interval = conf.interval;
		this.axes = conf.axes;
		this.metric = conf.metric;
	}

	get dimension () {
		return this.axes.length;
	}

	get size () {
		return this.axes.map(a => a.values.length)
	}

	get timeHeadingsLocation () {
		return this.getAxis('timeframe');
	}

	getAxis (name) {
		if (typeof name === 'number') {
			return name;
		}
		// easier to type
		if (name === 'time') {
			name = 'timeframe';
		}
		let dimension = -1;
		this.axes.some((dim, i) => {
			if (dim.type === name) {
				dimension = i;
				return true;
			}
		});
		return dimension;
	}

	convertTime (format) {
		if (this.timeHeadingsLocation === -1) {
			return this;
		}
		const axes = this.axes.slice();
		axes[this.timeHeadingsLocation] = {
			name: 'timeframe',
			values: axes[this.timeHeadingsLocation].values.map(obj => {
				return utils.formatTime(obj, this.interval, format)
			})
		};

		return this.clone({
			axes: axes
		});
	}

	// converts a table to a the sort of data structure graphing libraries typically require
	// i.e. a header row, then a number of rows with a heading in the first cell of each
	humanize (timeFormat) {
		if (this.dimension > 2) {
			throw 'can\'t humanize tables of greater than 2 dimensions'
		}
		const table = this.convertTime(timeFormat);
		const humanized = {};
		if (this.dimension === 0) {
			// just send back a single cell
			humanized.rows = [[table.metric, table.data]]
			return humanized;
		} else if (table.dimension === 1) {

			humanized.headings = [table.axes[0].property, table.metric]
			humanized.rows = table.axes[0].values.map((label, i) => {
				return [label, table.data[i]]; // this works because [i] notation works on an object or an array :-)
			})
		} else {
			const data = table.toNested();

			humanized.headings = [table.axes[0].property].concat(table.axes[1].values);
			humanized.rows = table.axes[0].values.map((label, i) => {
				return [label].concat(data[i]);
			})
		}
		const cols = humanized.headings.length;
		humanized.rows.forEach(r => {
			for(let i = 0; i<cols;i++) {
				r[i] = r[i] || '';
			}
		})
		return humanized;
	}

	// returns a nested 'matrix' of the data
	toNested (levels, startPoint) {
		if (startPoint === 'bottom') {
			return nestLowerLevels(this.data, this.dimension - (levels || 0));
		} else {
			return toNested(this.data, levels || 0);
		}
	}

	// iterates over every value in the flattened data stucture, calling func on each value
	// func expects (value, coordsString);
	cellIterator (func) {
		if (this.dimension === 0) {
			return func(this.data);
		}
		return Object.keys(this.data).reduce((obj, k) => {
			obj[k] = func(this.data[k], k);
			return obj;
		}, {});
	}

	reduce () {
		return postProcessing.reduce.apply(this, arguments);
	}

	// returns a new table with values rounded to n decimal points
	round (points) {
		points = points || 0;
		return this.clone({
			data: this.cellIterator(val => {
				return Math.round(val * Math.pow(10, points)) / Math.pow(10, points);
			})
		});
	}

	clone (overrides) {
		return new Table(Object.assign({}, this, overrides || {}));
	}

	// swaps two dimensions of the table
	// e.g. can convert a time x deviceType table to a deviceType x time table
	// a - index or name of the first dimension to move (if null, picks the first dimension available)
	// b - index or name of the second dimension to move (if null, picks the last dimension available)
	// method - shuffle or swap.
	// - swap does a straight swap of the dimensions, leaving all others unchanged
	// - shuffle (generally only applied when moving a dimension to be the first or last) shuffles all other dimensions along to make room
	switchDimensions (a, b, method) {
		if (this.dimensions < 2) {
			throw 'This is not a multidimensional table!';
		}
		method = method || ((a == null || b == null) ? 'shuffle' : 'swap');

	  a = a == null ? 0 : this.getAxis(a);
	  b = b == null ? this.dimension - 1 : this.getAxis(b);

	  if (a > this.dimensions || b > this.dimensions) {
			throw `This table does not have a ${Math.max(a, b)} dimension`;
		}

	  if (a === b) {
	  	return this;
	  }

	  if (a > b) {
	  	const tmp = a;
	  	a = b;
	  	b = tmp;
	  }

	  const axes = this.axes.slice();
	  const tmp = axes[a];

		if (method === 'swap') {
	    axes[a] = axes[b];
	    axes[b] = tmp;
  	} else if (method === 'shuffle') {
  		axes.splice(a, 1, axes[b]);
  		axes.splice(b, 1, tmp);
  	}

	  return this.clone({
			axes: axes,
			data: Object.keys(this.data).reduce((map, k) => {
		    const coords = k.split(',');
	    	const tmp = coords[a];
	    	if (method === 'swap') {
			    coords[a] = coords[b];
			    coords[b] = tmp;
	    	} else if (method === 'shuffle') {
	    		coords.splice(a, 1, coords[b]);
	    		coords.splice(b, 1, tmp);
	    	}

			  map[coords.join(',')] = this.data[k];
			  return map;
		  }, {})
		});
	}

}
module.exports = Table;
<commit_msg>Update table.js<commit_after>'use strict';

const utils = require('./utils');
const postProcessing = require('./post-processing');

// utility used to plot a value in a nested array
function plotInMatrix (matrix, coords, val, endDepth) {
	let i = coords.shift();
  let toModify = matrix;
	while (coords.length - endDepth) {
    toModify[i] = toModify[i] || [];
		toModify = toModify[i];
		i = coords.shift();
  }
	toModify[i] = val;
}

// converts flat data structure e.g. { '0,2,3': 'value'} into a matrix of nested arrays
// positive endDepth means it will leave the lower levels of the matrix flattened once n levels have been nested
function toNested (data, endDepth) {
	const matrix = [];
	Object.keys(data).forEach(coords => {
		plotInMatrix(matrix, coords.split(','), data[coords], endDepth || 0);
	});
	return matrix;
}

// converts flat data structure e.g. { '0,2,3': 'value'} into a matrix of nested arrays
// similar to the above, but starts from the leaves rather than the root of the matrix
// the above function, when endLevel is defined, will return nested arrays which contain flat data structures as their leaves
// this function, on the other hand, will return a flat data structure with nested arrays as the leaves
function nestLowerLevels (flatData, endLevel) {
	const obj = {};
	Object.keys(flatData).forEach(k => {
		const coords = k.split(',');
		const bucketCoord = coords.slice(0, endLevel);
		obj[bucketCoord] = obj[bucketCoord] || {};
		obj[bucketCoord][coords.slice(endLevel)] = flatData[k];
	})
	Object.keys(obj).forEach(k => {
		obj[k] = toNested(obj[k]);
	})
	return obj
}

class Table {
	constructor (conf) {
		this.name = conf.name;
		this.data = conf.data;
		this.interval = conf.interval;
		this.axes = conf.axes;
		this.metric = conf.metric;
	}

	get dimension () {
		return this.axes.length;
	}

	get size () {
		return this.axes.map(a => a.values.length)
	}

	get timeHeadingsLocation () {
		return this.getAxis('timeframe');
	}

	getAxis (name) {
		if (typeof name === 'number') {
			return name;
		}
		// easier to type
		if (name === 'time') {
			name = 'timeframe';
		}
		let dimension = -1;
		this.axes.some((dim, i) => {
			if (dim.type === name) {
				dimension = i;
				return true;
			}
		});
		return dimension;
	}

	convertTime (format) {
		if (this.timeHeadingsLocation === -1) {
			return this;
		}
		const axes = this.axes.slice();
		axes[this.timeHeadingsLocation] = {
			name: 'timeframe',
			values: axes[this.timeHeadingsLocation].values.map(obj => {
				return utils.formatTime(obj, this.interval, format)
			})
		};

		return this.clone({
			axes: axes
		});
	}

	// converts a table to a the sort of data structure graphing libraries typically require
	// i.e. a header row, then a number of rows with a heading in the first cell of each
	humanize (timeFormat) {
		if (this.dimension > 2) {
			throw 'can\'t humanize tables of greater than 2 dimensions'
		}
		const table = this.convertTime(timeFormat);
		const humanized = {};
		if (this.dimension === 0) {
			// just send back a single cell
			humanized.rows = [[table.metric, table.data]]
			return humanized;
		} else if (table.dimension === 1) {

			humanized.headings = [table.axes[0].property, table.metric]
			humanized.rows = table.axes[0].values.map((label, i) => {
				return [label, table.data[i]]; // this works because [i] notation works on an object or an array :-)
			})
		} else {
			const data = table.toNested();

			humanized.headings = [table.axes[0].property].concat(table.axes[1].values);
			humanized.rows = table.axes[0].values.map((label, i) => {
				return [label].concat(data[i]);
			})
		}
		const cols = humanized.headings.length;
		humanized.rows.forEach(r => {
			for(let i = 0; i<cols;i++) {
				r[i] = r[i] || 0;
			}
		})
		return humanized;
	}

	// returns a nested 'matrix' of the data
	toNested (levels, startPoint) {
		if (startPoint === 'bottom') {
			return nestLowerLevels(this.data, this.dimension - (levels || 0));
		} else {
			return toNested(this.data, levels || 0);
		}
	}

	// iterates over every value in the flattened data stucture, calling func on each value
	// func expects (value, coordsString);
	cellIterator (func) {
		if (this.dimension === 0) {
			return func(this.data);
		}
		return Object.keys(this.data).reduce((obj, k) => {
			obj[k] = func(this.data[k], k);
			return obj;
		}, {});
	}

	reduce () {
		return postProcessing.reduce.apply(this, arguments);
	}

	// returns a new table with values rounded to n decimal points
	round (points) {
		points = points || 0;
		return this.clone({
			data: this.cellIterator(val => {
				return Math.round(val * Math.pow(10, points)) / Math.pow(10, points);
			})
		});
	}

	clone (overrides) {
		return new Table(Object.assign({}, this, overrides || {}));
	}

	// swaps two dimensions of the table
	// e.g. can convert a time x deviceType table to a deviceType x time table
	// a - index or name of the first dimension to move (if null, picks the first dimension available)
	// b - index or name of the second dimension to move (if null, picks the last dimension available)
	// method - shuffle or swap.
	// - swap does a straight swap of the dimensions, leaving all others unchanged
	// - shuffle (generally only applied when moving a dimension to be the first or last) shuffles all other dimensions along to make room
	switchDimensions (a, b, method) {
		if (this.dimensions < 2) {
			throw 'This is not a multidimensional table!';
		}
		method = method || ((a == null || b == null) ? 'shuffle' : 'swap');

	  a = a == null ? 0 : this.getAxis(a);
	  b = b == null ? this.dimension - 1 : this.getAxis(b);

	  if (a > this.dimensions || b > this.dimensions) {
			throw `This table does not have a ${Math.max(a, b)} dimension`;
		}

	  if (a === b) {
	  	return this;
	  }

	  if (a > b) {
	  	const tmp = a;
	  	a = b;
	  	b = tmp;
	  }

	  const axes = this.axes.slice();
	  const tmp = axes[a];

		if (method === 'swap') {
	    axes[a] = axes[b];
	    axes[b] = tmp;
  	} else if (method === 'shuffle') {
  		axes.splice(a, 1, axes[b]);
  		axes.splice(b, 1, tmp);
  	}

	  return this.clone({
			axes: axes,
			data: Object.keys(this.data).reduce((map, k) => {
		    const coords = k.split(',');
	    	const tmp = coords[a];
	    	if (method === 'swap') {
			    coords[a] = coords[b];
			    coords[b] = tmp;
	    	} else if (method === 'shuffle') {
	    		coords.splice(a, 1, coords[b]);
	    		coords.splice(b, 1, tmp);
	    	}

			  map[coords.join(',')] = this.data[k];
			  return map;
		  }, {})
		});
	}

}
module.exports = Table;
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const promisify = require("es6-promisify");

const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);
const readFile = promisify(fs.readFile);

function readJSON(filename){
	return new Promise(function (fulfill, reject){
		readFile(filename, 'utf8').then(function (res){
			try {
				fulfill(JSON.parse(res));
			} catch (ex) {
				reject(ex);
			}
		}).catch(reject);
	});
}

function listDirectory(dirPath, allowedFileTypes){
	return readdir(dirPath)
		.then(function(paths){
			return paths.map(function(filepath){
				return path.resolve(dirPath, filepath);
			}).filter(function(absPath){
				if (allowedFileTypes){
					return allowedFileTypes.includes(path.extname(absPath));
				} else {
					return true;
				}
			});
		});
}

let args = [... process.argv];
args.shift();
args.shift();

module.exports = { readJSON, args, listDirectory };<commit_msg>neaten up readJSON<commit_after>const fs = require('fs');
const path = require('path');
const promisify = require("es6-promisify");

const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);
const readFile = promisify(fs.readFile);

function readJSON(filename){
	return readFile(filename, 'utf8').then(function (res){
		return JSON.parse(res);
	});
}

function listDirectory(dirPath, allowedFileTypes){
	return readdir(dirPath)
		.then(function(paths){
			return paths.map(function(filepath){
				return path.resolve(dirPath, filepath);
			}).filter(function(absPath){
				if (allowedFileTypes){
					return allowedFileTypes.includes(path.extname(absPath));
				} else {
					return true;
				}
			});
		});
}

let args = [... process.argv];
args.shift();
args.shift();

module.exports = { readJSON, readFile, args, listDirectory };<|endoftext|>


<commit_before>import request from 'request';
import fs from 'fs';
import yaml from 'js-yaml';
import merge from 'deepmerge';
import path from 'path';
import dns from 'dns';
import { series } from 'async';

import AwsMetadata from './AwsMetadata';
import Logger from './Logger';
import defaultConfig from './defaultConfig';

function noop() {}

/*
  Eureka JS client
  This module handles registration with a Eureka server, as well as heartbeats
  for reporting instance health.
*/

function getYaml(file) {
  let yml = {};
  try {
    yml = yaml.safeLoad(fs.readFileSync(file, 'utf8'));
  } catch (e) {
    // Ignore YAML load error.
  }
  return yml;
}

export default class Eureka {

  constructor(config = {}) {
    // Allow passing in a custom logger:
    this.logger = config.logger || new Logger();

    this.logger.debug('initializing eureka client');

    // Load up the current working directory and the environment:
    const cwd = config.cwd || process.cwd();
    const env = process.env.NODE_ENV || 'development';

    const filename = config.filename || 'eureka-client';

    // Load in the configuration files:
    this.config = merge(defaultConfig, getYaml(path.join(cwd, `${filename}.yml`)));
    this.config = merge(this.config, getYaml(path.join(cwd, `${filename}-${env}.yml`)));

    // Finally, merge in the passed configuration:
    this.config = merge(this.config, config);

    // Validate the provided the values we need:
    this.validateConfig(this.config);

    if (this.amazonDataCenter) {
      this.metadataClient = new AwsMetadata({
        logger: this.logger,
      });
    }

    this.cache = {
      app: {},
      vip: {},
    };
  }

  /*
    Helper method to get the instance ID. If the datacenter is AWS, this will be the
    instance-id in the metadata. Else, it's the hostName.
  */
  get instanceId() {
    if (this.amazonDataCenter) {
      return this.config.instance.dataCenterInfo.metadata['instance-id'];
    }
    return this.config.instance.hostName;
  }

  /*
    Helper method to determine if this is an AWS datacenter.
  */
  get amazonDataCenter() {
    return (
      this.config.instance.dataCenterInfo.name &&
      this.config.instance.dataCenterInfo.name.toLowerCase() === 'amazon'
    );
  }

  /*
    Build the base Eureka server URL + path
  */
  buildEurekaUrl(callback = noop) {
    this.lookupCurrentEurekaHost(eurekaHost => {
      const { port, servicePath, ssl } = this.config.eureka;
      const host = ssl ? 'https' : 'http';
      callback(`${host}://${eurekaHost}:${port}${servicePath}`);
    });
  }

  /*
    Registers instance with Eureka, begins heartbeats, and fetches registry.
  */
  start(callback = noop) {
    series([
      done => {
        if (this.metadataClient && this.config.eureka.fetchMetadata) {
          return this.addInstanceMetadata(done);
        }
        done();
      },
      done => {
        this.register(done);
      },
      done => {
        if (this.config.eureka.fetchRegistry) {
          return this.fetchRegistry(done);
        }
        done();
      },
    ], callback);
  }

  /*
    De-registers instance with Eureka, stops heartbeats / registry fetches.
  */
  stop(callback = noop) {
    this.deregister(callback);
    clearInterval(this.heartbeat);
    clearInterval(this.registryFetch);
  }

  /*
    Validates client configuration.
  */
  validateConfig(config) {
    function validate(namespace, key) {
      if (!config[namespace][key]) {
        throw new TypeError(`Missing "${namespace}.${key}" config value.`);
      }
    }

    validate('instance', 'app');
    validate('instance', 'vipAddress');
    validate('instance', 'port');
    validate('instance', 'dataCenterInfo');
    validate('eureka', 'host');
    validate('eureka', 'port');
  }

  /*
    Registers with the Eureka server and initializes heartbeats on registration success.
  */
  register(callback = noop) {
    this.config.instance.status = 'UP';
    const connectionTimeout = setTimeout(() => {
      this.logger.warn('It looks like it\'s taking a while to register with ' +
      'Eureka. This usually means there is an issue connecting to the host ' +
      'specified. Start application with NODE_DEBUG=request for more logging.');
    }, 10000);
    this.buildEurekaUrl(eurekaUrl => {
      request.post({
        url: eurekaUrl + this.config.instance.app,
        json: true,
        body: { instance: this.config.instance },
      }, (error, response, body) => {
        clearTimeout(connectionTimeout);
        if (!error && response.statusCode === 204) {
          this.logger.info(
            'registered with eureka: ',
            `${this.config.instance.app}/${this.instanceId}`
          );
          this.startHeartbeats();
          if (this.config.eureka.fetchRegistry) {
            this.startRegistryFetches();
          }
          return callback(null);
        } else if (error) {
          return callback(error);
        }
        return callback(
          new Error(`eureka registration FAILED: status: ${response.statusCode} body: ${body}`)
        );
      });
    });
  }

  /*
    De-registers with the Eureka server and stops heartbeats.
  */
  deregister(callback = noop) {
    this.buildEurekaUrl(eurekaUrl => {
      request.del({
        url: `${eurekaUrl}${this.config.instance.app}/${this.instanceId}`,
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          this.logger.info(
            'de-registered with eureka: ',
            `${this.config.instance.app}/${this.instanceId}`
          );
          return callback(null);
        } else if (error) {
          return callback(error);
        }
        return callback(
          new Error(`eureka deregistration FAILED: status: ${response.statusCode} body: ${body}`)
        );
      });
    });
  }

  /*
    Sets up heartbeats on interval for the life of the application.
    Heartbeat interval by setting configuration property: eureka.heartbeatInterval
  */
  startHeartbeats() {
    this.heartbeat = setInterval(() => {
      this.renew();
    }, this.config.eureka.heartbeatInterval);
  }

  renew() {
    this.buildEurekaUrl(eurekaUrl => {
      request.put({
        url: `${eurekaUrl}${this.config.instance.app}/${this.instanceId}`,
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          this.logger.debug('eureka heartbeat success');
        } else {
          if (error) {
            this.logger.error('An error in the request occured.', error);
          }
          this.logger.warn(
            'eureka heartbeat FAILED, will retry.',
            `status: ${response ? response.statusCode : 'unknown'} body: ${body}`
          );
        }
      });
    });
  }

  /*
    Sets up registry fetches on interval for the life of the application.
    Registry fetch interval setting configuration property: eureka.registryFetchInterval
  */
  startRegistryFetches() {
    this.registryFetch = setInterval(() => {
      this.fetchRegistry();
    }, this.config.eureka.registryFetchInterval);
  }

  /*
    Retrieves a list of instances from Eureka server given an appId
  */
  getInstancesByAppId(appId) {
    if (!appId) {
      throw new RangeError('Unable to query instances with no appId');
    }
    const instances = this.cache.app[appId.toUpperCase()];
    if (!instances) {
      this.logger.warn(`Unable to retrieve instances for appId: ${appId}`);
    }
    return instances;
  }

  /*
    Retrieves a list of instances from Eureka server given a vipAddress
   */
  getInstancesByVipAddress(vipAddress) {
    if (!vipAddress) {
      throw new RangeError('Unable to query instances with no vipAddress');
    }
    const instances = this.cache.vip[vipAddress];
    if (!instances) {
      this.logger.warn(`Unable to retrieves instances for vipAddress: ${vipAddress}`);
    }
    return instances;
  }

  /*
    Retrieves all applications registered with the Eureka server
   */
  fetchRegistry(callback = noop) {
    this.buildEurekaUrl(eurekaUrl => {
      request.get({
        url: eurekaUrl,
        headers: {
          Accept: 'application/json',
        },
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          this.logger.debug('retrieved registry successfully');
          this.transformRegistry(JSON.parse(body));
          return callback(null);
        } else if (error) {
          return callback(error);
        }
        callback(new Error('Unable to retrieve registry from Eureka server'));
      });
    });
  }

  /*
    Transforms the given registry and caches the registry locally
   */
  transformRegistry(registry) {
    if (!registry) {
      this.logger.warn('Unable to transform empty registry');
    } else {
      const newCache = { app: {}, vip: {} };
      if (!registry.applications.application) {
        return;
      }
      if (registry.applications.application.length) {
        for (let i = 0; i < registry.applications.application.length; i++) {
          const app = registry.applications.application[i];
          this.transformApp(app, newCache);
        }
      } else {
        this.transformApp(registry.applications.application, newCache);
      }
      this.cache = newCache;
    }
  }

  /*
    Transforms the given application and places in client cache. If an application
      has a single instance, the instance is placed into the cache as an array of one
   */
  transformApp(app, cache) {
    if (app.instance.length) {
      cache.app[app.name.toUpperCase()] = app.instance;
      cache.vip[app.instance[0].vipAddress] = app.instance;
    } else {
      const instances = [app.instance];
      cache.vip[app.instance.vipAddress] = instances;
      cache.app[app.name.toUpperCase()] = instances;
    }
  }

  /*
    Fetches the metadata using the built-in client and updates the instance
    configuration with the public hostname and IP address. A string replacement
    is done on the healthCheckUrl and statusPageUrl so that users can define
    the URLs with a placeholder for the host ('__HOST__'). This allows
    flexibility since the host isn't known until the metadata is fetched.

    This will only get called when dataCenterInfo.name is Amazon, but you can
    set config.eureka.fetchMetadata to false if you want to provide your own
    metadata in AWS environments.
  */
  addInstanceMetadata(callback = noop) {
    this.metadataClient.fetchMetadata(metadataResult => {
      this.config.instance.dataCenterInfo.metadata = merge(
        this.config.instance.dataCenterInfo.metadata,
        metadataResult
      );
      this.config.instance.hostName = metadataResult['public-hostname'];
      this.config.instance.ipAddr = metadataResult['public-ipv4'];

      if (this.config.instance.statusPageUrl) {
        const { statusPageUrl } = this.config.instance;
        const replacedUrl = statusPageUrl.replace('__HOST__', metadataResult['public-hostname']);
        this.config.instance.statusPageUrl = replacedUrl;
      }
      if (this.config.instance.healthCheckUrl) {
        const { healthCheckUrl } = this.config.instance;
        const replacedUrl = healthCheckUrl.replace('__HOST__', metadataResult['public-hostname']);
        this.config.instance.healthCheckUrl = replacedUrl;
      }

      callback();
    });
  }

  /*
    Returns the Eureka host. This method is async because potentially we might have to
    execute DNS lookups which is an async network operation.
  */
  lookupCurrentEurekaHost(callback = noop) {
    if (this.amazonDataCenter && this.config.eureka.useDns) {
      this.locateEurekaHostUsingDns(resolvedHost => callback(resolvedHost));
    } else {
      return callback(this.config.eureka.host);
    }
  }

  /*
    Locates a Eureka host using DNS lookups. The DNS records are looked up by a naming
    convention and TXT records must be created according to the Eureka Wiki here:
    https://github.com/Netflix/eureka/wiki/Configuring-Eureka-in-AWS-Cloud

    Naming convention: txt.<REGION>.<HOST>
   */
  locateEurekaHostUsingDns(callback = noop) {
    const { ec2Region, host } = this.config.eureka;
    if (!ec2Region) {
      throw new Error(
        'EC2 region was undefined. ' +
        'config.eureka.ec2Region must be set to resolve Eureka using DNS records.'
      );
    }
    dns.resolveTxt(`txt.${ec2Region}.${host}`, (err, addresses) => {
      if (err) {
        throw new Error(
          `Error resolving eureka server list for region [${ec2Region}] using DNS: [${err}]`
        );
      }
      const random = Math.floor(Math.random() * addresses[0].length);
      dns.resolveTxt(`txt.${addresses[0][random]}`, (resolveErr, results) => {
        if (resolveErr) {
          throw new Error(`Error locating eureka server using DNS: [${resolveErr}]`);
        }
        this.logger.debug('Found Eureka Server @ ', results);
        callback([].concat(...results).shift());
      });
    });
  }

}
<commit_msg>switching to forEach<commit_after>import request from 'request';
import fs from 'fs';
import yaml from 'js-yaml';
import merge from 'deepmerge';
import path from 'path';
import dns from 'dns';
import { series } from 'async';

import AwsMetadata from './AwsMetadata';
import Logger from './Logger';
import defaultConfig from './defaultConfig';

function noop() {}

/*
  Eureka JS client
  This module handles registration with a Eureka server, as well as heartbeats
  for reporting instance health.
*/

function getYaml(file) {
  let yml = {};
  try {
    yml = yaml.safeLoad(fs.readFileSync(file, 'utf8'));
  } catch (e) {
    // Ignore YAML load error.
  }
  return yml;
}

export default class Eureka {

  constructor(config = {}) {
    // Allow passing in a custom logger:
    this.logger = config.logger || new Logger();

    this.logger.debug('initializing eureka client');

    // Load up the current working directory and the environment:
    const cwd = config.cwd || process.cwd();
    const env = process.env.NODE_ENV || 'development';

    const filename = config.filename || 'eureka-client';

    // Load in the configuration files:
    this.config = merge(defaultConfig, getYaml(path.join(cwd, `${filename}.yml`)));
    this.config = merge(this.config, getYaml(path.join(cwd, `${filename}-${env}.yml`)));

    // Finally, merge in the passed configuration:
    this.config = merge(this.config, config);

    // Validate the provided the values we need:
    this.validateConfig(this.config);

    if (this.amazonDataCenter) {
      this.metadataClient = new AwsMetadata({
        logger: this.logger,
      });
    }

    this.cache = {
      app: {},
      vip: {},
    };
  }

  /*
    Helper method to get the instance ID. If the datacenter is AWS, this will be the
    instance-id in the metadata. Else, it's the hostName.
  */
  get instanceId() {
    if (this.amazonDataCenter) {
      return this.config.instance.dataCenterInfo.metadata['instance-id'];
    }
    return this.config.instance.hostName;
  }

  /*
    Helper method to determine if this is an AWS datacenter.
  */
  get amazonDataCenter() {
    return (
      this.config.instance.dataCenterInfo.name &&
      this.config.instance.dataCenterInfo.name.toLowerCase() === 'amazon'
    );
  }

  /*
    Build the base Eureka server URL + path
  */
  buildEurekaUrl(callback = noop) {
    this.lookupCurrentEurekaHost(eurekaHost => {
      const { port, servicePath, ssl } = this.config.eureka;
      const host = ssl ? 'https' : 'http';
      callback(`${host}://${eurekaHost}:${port}${servicePath}`);
    });
  }

  /*
    Registers instance with Eureka, begins heartbeats, and fetches registry.
  */
  start(callback = noop) {
    series([
      done => {
        if (this.metadataClient && this.config.eureka.fetchMetadata) {
          return this.addInstanceMetadata(done);
        }
        done();
      },
      done => {
        this.register(done);
      },
      done => {
        if (this.config.eureka.fetchRegistry) {
          return this.fetchRegistry(done);
        }
        done();
      },
    ], callback);
  }

  /*
    De-registers instance with Eureka, stops heartbeats / registry fetches.
  */
  stop(callback = noop) {
    this.deregister(callback);
    clearInterval(this.heartbeat);
    clearInterval(this.registryFetch);
  }

  /*
    Validates client configuration.
  */
  validateConfig(config) {
    function validate(namespace, key) {
      if (!config[namespace][key]) {
        throw new TypeError(`Missing "${namespace}.${key}" config value.`);
      }
    }

    validate('instance', 'app');
    validate('instance', 'vipAddress');
    validate('instance', 'port');
    validate('instance', 'dataCenterInfo');
    validate('eureka', 'host');
    validate('eureka', 'port');
  }

  /*
    Registers with the Eureka server and initializes heartbeats on registration success.
  */
  register(callback = noop) {
    this.config.instance.status = 'UP';
    const connectionTimeout = setTimeout(() => {
      this.logger.warn('It looks like it\'s taking a while to register with ' +
      'Eureka. This usually means there is an issue connecting to the host ' +
      'specified. Start application with NODE_DEBUG=request for more logging.');
    }, 10000);
    this.buildEurekaUrl(eurekaUrl => {
      request.post({
        url: eurekaUrl + this.config.instance.app,
        json: true,
        body: { instance: this.config.instance },
      }, (error, response, body) => {
        clearTimeout(connectionTimeout);
        if (!error && response.statusCode === 204) {
          this.logger.info(
            'registered with eureka: ',
            `${this.config.instance.app}/${this.instanceId}`
          );
          this.startHeartbeats();
          if (this.config.eureka.fetchRegistry) {
            this.startRegistryFetches();
          }
          return callback(null);
        } else if (error) {
          return callback(error);
        }
        return callback(
          new Error(`eureka registration FAILED: status: ${response.statusCode} body: ${body}`)
        );
      });
    });
  }

  /*
    De-registers with the Eureka server and stops heartbeats.
  */
  deregister(callback = noop) {
    this.buildEurekaUrl(eurekaUrl => {
      request.del({
        url: `${eurekaUrl}${this.config.instance.app}/${this.instanceId}`,
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          this.logger.info(
            'de-registered with eureka: ',
            `${this.config.instance.app}/${this.instanceId}`
          );
          return callback(null);
        } else if (error) {
          return callback(error);
        }
        return callback(
          new Error(`eureka deregistration FAILED: status: ${response.statusCode} body: ${body}`)
        );
      });
    });
  }

  /*
    Sets up heartbeats on interval for the life of the application.
    Heartbeat interval by setting configuration property: eureka.heartbeatInterval
  */
  startHeartbeats() {
    this.heartbeat = setInterval(() => {
      this.renew();
    }, this.config.eureka.heartbeatInterval);
  }

  renew() {
    this.buildEurekaUrl(eurekaUrl => {
      request.put({
        url: `${eurekaUrl}${this.config.instance.app}/${this.instanceId}`,
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          this.logger.debug('eureka heartbeat success');
        } else {
          if (error) {
            this.logger.error('An error in the request occured.', error);
          }
          this.logger.warn(
            'eureka heartbeat FAILED, will retry.',
            `status: ${response ? response.statusCode : 'unknown'} body: ${body}`
          );
        }
      });
    });
  }

  /*
    Sets up registry fetches on interval for the life of the application.
    Registry fetch interval setting configuration property: eureka.registryFetchInterval
  */
  startRegistryFetches() {
    this.registryFetch = setInterval(() => {
      this.fetchRegistry();
    }, this.config.eureka.registryFetchInterval);
  }

  /*
    Retrieves a list of instances from Eureka server given an appId
  */
  getInstancesByAppId(appId) {
    if (!appId) {
      throw new RangeError('Unable to query instances with no appId');
    }
    const instances = this.cache.app[appId.toUpperCase()];
    if (!instances) {
      this.logger.warn(`Unable to retrieve instances for appId: ${appId}`);
    }
    return instances;
  }

  /*
    Retrieves a list of instances from Eureka server given a vipAddress
   */
  getInstancesByVipAddress(vipAddress) {
    if (!vipAddress) {
      throw new RangeError('Unable to query instances with no vipAddress');
    }
    const instances = this.cache.vip[vipAddress];
    if (!instances) {
      this.logger.warn(`Unable to retrieves instances for vipAddress: ${vipAddress}`);
    }
    return instances;
  }

  /*
    Retrieves all applications registered with the Eureka server
   */
  fetchRegistry(callback = noop) {
    this.buildEurekaUrl(eurekaUrl => {
      request.get({
        url: eurekaUrl,
        headers: {
          Accept: 'application/json',
        },
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          this.logger.debug('retrieved registry successfully');
          this.transformRegistry(JSON.parse(body));
          return callback(null);
        } else if (error) {
          return callback(error);
        }
        callback(new Error('Unable to retrieve registry from Eureka server'));
      });
    });
  }

  /*
    Transforms the given registry and caches the registry locally
   */
  transformRegistry(registry) {
    if (!registry) {
      this.logger.warn('Unable to transform empty registry');
    } else {
      if (!registry.applications.application) {
        return;
      }
      const newCache = { app: {}, vip: {} };
      if (registry.applications.application.length) {
        registry.applications.application.forEach((app) => {
          this.transformApp(app, newCache);
        });
      } else {
        this.transformApp(registry.applications.application, newCache);
      }
      this.cache = newCache;
    }
  }

  /*
    Transforms the given application and places in client cache. If an application
      has a single instance, the instance is placed into the cache as an array of one
   */
  transformApp(app, cache) {
    if (app.instance.length) {
      cache.app[app.name.toUpperCase()] = app.instance;
      cache.vip[app.instance[0].vipAddress] = app.instance;
    } else {
      const instances = [app.instance];
      cache.vip[app.instance.vipAddress] = instances;
      cache.app[app.name.toUpperCase()] = instances;
    }
  }

  /*
    Fetches the metadata using the built-in client and updates the instance
    configuration with the public hostname and IP address. A string replacement
    is done on the healthCheckUrl and statusPageUrl so that users can define
    the URLs with a placeholder for the host ('__HOST__'). This allows
    flexibility since the host isn't known until the metadata is fetched.

    This will only get called when dataCenterInfo.name is Amazon, but you can
    set config.eureka.fetchMetadata to false if you want to provide your own
    metadata in AWS environments.
  */
  addInstanceMetadata(callback = noop) {
    this.metadataClient.fetchMetadata(metadataResult => {
      this.config.instance.dataCenterInfo.metadata = merge(
        this.config.instance.dataCenterInfo.metadata,
        metadataResult
      );
      this.config.instance.hostName = metadataResult['public-hostname'];
      this.config.instance.ipAddr = metadataResult['public-ipv4'];

      if (this.config.instance.statusPageUrl) {
        const { statusPageUrl } = this.config.instance;
        const replacedUrl = statusPageUrl.replace('__HOST__', metadataResult['public-hostname']);
        this.config.instance.statusPageUrl = replacedUrl;
      }
      if (this.config.instance.healthCheckUrl) {
        const { healthCheckUrl } = this.config.instance;
        const replacedUrl = healthCheckUrl.replace('__HOST__', metadataResult['public-hostname']);
        this.config.instance.healthCheckUrl = replacedUrl;
      }

      callback();
    });
  }

  /*
    Returns the Eureka host. This method is async because potentially we might have to
    execute DNS lookups which is an async network operation.
  */
  lookupCurrentEurekaHost(callback = noop) {
    if (this.amazonDataCenter && this.config.eureka.useDns) {
      this.locateEurekaHostUsingDns(resolvedHost => callback(resolvedHost));
    } else {
      return callback(this.config.eureka.host);
    }
  }

  /*
    Locates a Eureka host using DNS lookups. The DNS records are looked up by a naming
    convention and TXT records must be created according to the Eureka Wiki here:
    https://github.com/Netflix/eureka/wiki/Configuring-Eureka-in-AWS-Cloud

    Naming convention: txt.<REGION>.<HOST>
   */
  locateEurekaHostUsingDns(callback = noop) {
    const { ec2Region, host } = this.config.eureka;
    if (!ec2Region) {
      throw new Error(
        'EC2 region was undefined. ' +
        'config.eureka.ec2Region must be set to resolve Eureka using DNS records.'
      );
    }
    dns.resolveTxt(`txt.${ec2Region}.${host}`, (err, addresses) => {
      if (err) {
        throw new Error(
          `Error resolving eureka server list for region [${ec2Region}] using DNS: [${err}]`
        );
      }
      const random = Math.floor(Math.random() * addresses[0].length);
      dns.resolveTxt(`txt.${addresses[0][random]}`, (resolveErr, results) => {
        if (resolveErr) {
          throw new Error(`Error locating eureka server using DNS: [${resolveErr}]`);
        }
        this.logger.debug('Found Eureka Server @ ', results);
        callback([].concat(...results).shift());
      });
    });
  }

}
<|endoftext|>


<commit_before>var Promise = window.Promise || require('../vendor/native-promise-only');

function decode(s) {
  return decodeURIComponent((s || '').replace( /\+/g, " " ));
}

function queryParameters(queryString) {
  var result = {},
      keyValuePairs,
      keyAndValue,
      key,
      value;

  queryString = queryString ||
    ( document.location.search || '' ).slice(1);

  if (queryString.length === 0) { return result; }

  keyValuePairs = queryString.split('&');

  for (var i = 0; i < keyValuePairs.length; i++) {
    keyAndValue = keyValuePairs[i].split('=');
    key   = decode(keyAndValue[0]);
    value = decode(keyAndValue[1]) || '';
    result[key] = value;
  }

  return result;
}

function isPromise(obj) {
  return obj instanceof Promise ||
    !!obj && obj.then && typeof obj.then === 'function';
}

function isFalsy(val) {
  return !val ||
    val === false ||
    val instanceof Error ||
    typeof val === 'string';
}

// When receives a list of things to wrap as Promises.
// it will then wait for:
//  a) all to settle
//  b) first to reject
//  c) first to settle with a falsy value
// c) is sub-optimal, since it has a specific narrow use-case
// for save hooks
function when(items) {
  items = !items ? [] : items;
  var resolveAll, rejectAll,
      allResolvedPromise = new Promise(function(resolve, reject) {
        resolveAll = resolve;
        rejectAll = reject;
      });

  var remaining = 0,
      settledWith = [],
      itemsCopy = Array.isArray(items) ?
        items.slice() :
        [items];

  var resolveWith = function(data, index) {
    settledWith[index] = data;

    if (--remaining <= 0) {
      resolveAll(settledWith);
    }
  };

  remaining = itemsCopy.length;

  if (remaining <= 0) {
    resolveAll();
    return allResolvedPromise;
  }

  itemsCopy.forEach(function(item, index) {
    var promise;
    if (isPromise(item)) {
      promise = item;
    } else if (typeof item === 'function') {
      var res;
      try {
        res = item();
        if (isPromise(res)) {
          promise = res;
        } else {
          promise = new Promise(function(resolve) {
            resolve(res);
          });
        }
      } catch(err) {
        promise = new Promise(function(resolve, reject) {
          reject(err);
        });
      }
    } else {
      promise = new Promise(function(resolve, reject) {
        isFalsy(item) ?
          reject(item) :
          resolve(item);
      });
    }
    promise.then(
      function(data) {
        resolveWith(data, index);
      }
    ).catch(rejectAll.bind(rejectAll));
    return promise;
  });

  return allResolvedPromise;
}

module.exports = {
  queryParameters: queryParameters,
  when: when
};
<commit_msg>Simplify isFalsy test and items default value<commit_after>var Promise = window.Promise || require('../vendor/native-promise-only');

function decode(s) {
  return decodeURIComponent((s || '').replace( /\+/g, " " ));
}

function queryParameters(queryString) {
  var result = {},
      keyValuePairs,
      keyAndValue,
      key,
      value;

  queryString = queryString ||
    ( document.location.search || '' ).slice(1);

  if (queryString.length === 0) { return result; }

  keyValuePairs = queryString.split('&');

  for (var i = 0; i < keyValuePairs.length; i++) {
    keyAndValue = keyValuePairs[i].split('=');
    key   = decode(keyAndValue[0]);
    value = decode(keyAndValue[1]) || '';
    result[key] = value;
  }

  return result;
}

function isPromise(obj) {
  return obj instanceof Promise ||
    !!obj && obj.then && typeof obj.then === 'function';
}

function isFalsy(val) {
  return !val ||
    val instanceof Error ||
    typeof val === 'string';
}

// When receives a list of things to wrap as Promises.
// it will then wait for:
//  a) all to settle
//  b) first to reject
//  c) first to settle with a falsy value
// c) is sub-optimal, since it has a specific narrow use-case
// for save hooks
function when(items) {
  items = items || [];
  var resolveAll, rejectAll,
      allResolvedPromise = new Promise(function(resolve, reject) {
        resolveAll = resolve;
        rejectAll = reject;
      });

  var remaining = 0,
      settledWith = [],
      itemsCopy = Array.isArray(items) ?
        items.slice() :
        [items];

  var resolveWith = function(data, index) {
    settledWith[index] = data;

    if (--remaining <= 0) {
      resolveAll(settledWith);
    }
  };

  remaining = itemsCopy.length;

  if (remaining <= 0) {
    resolveAll();
    return allResolvedPromise;
  }

  itemsCopy.forEach(function(item, index) {
    var promise;
    if (isPromise(item)) {
      promise = item;
    } else if (typeof item === 'function') {
      var res;
      try {
        res = item();
        if (isPromise(res)) {
          promise = res;
        } else {
          promise = new Promise(function(resolve) {
            resolve(res);
          });
        }
      } catch(err) {
        promise = new Promise(function(resolve, reject) {
          reject(err);
        });
      }
    } else {
      promise = new Promise(function(resolve, reject) {
        isFalsy(item) ?
          reject(item) :
          resolve(item);
      });
    }
    promise.then(
      function(data) {
        resolveWith(data, index);
      }
    ).catch(rejectAll.bind(rejectAll));
    return promise;
  });

  return allResolvedPromise;
}

module.exports = {
  queryParameters: queryParameters,
  when: when
};
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');

const DEFAULT_CONFIG_IGNORE_PATHS = [];
const DEFAULT_CONFIG_PARSER = 'dox';
const DEFAULT_CONFIG_PLUGIN = 'markdown';

/**
 * Finds package.json file from either the directory the script was called from or a supplied path.
 *
 *     console.log(findPackageFileInPath());
 *     console.log(findPackageFileInPath('./package.json'));
 *     console.log(findPackageFileInPath('~/git/github/doxdox/'));
 *
 * @param {String} [input] Directory or file.
 * @return {String} Path to package.json file.
 * @public
 */

const findPackageFileInPath = input => {

    if (!input) {

        input = process.cwd();

    }

    try {

        const stat = fs.statSync(input);

        if (stat.isFile()) {

            return path.resolve(path.join(path.dirname(input), '/package.json'));

        } else if (stat.isDirectory()) {

            return path.resolve(path.join(input, '/package.json'));

        }

    } catch (err) {

        process.stderr.write(`${err.toString()}\n`);

    }

    return null;

};

/**
 * Format an array of directories and/or files to be ignored by globby by adding a "!" at the beginning of each path.
 *
 *     console.log(formatPathsArrayToIgnore(['./src']));
 *
 * @param {Array} paths Array of directories and/or files.
 * @return {Array} Modified array of directories and/or files.
 * @public
 */

const formatPathsArrayToIgnore = paths =>
    paths.map(path => `!${path.replace(/^!/, '')}`);

/**
 * Sets default configuration values.
 *
 *     console.log(setConfigDefaults({}));
 *
 * @param {Object} config Custom configuration object.
 * @return {Object} Modified configuration object.
 * @public
 */

const setConfigDefaults = config => Object.assign({}, {
    'ignore': DEFAULT_CONFIG_IGNORE_PATHS,
    'parser': DEFAULT_CONFIG_PARSER,
    'plugin': DEFAULT_CONFIG_PLUGIN
}, config);

module.exports = {
    findPackageFileInPath,
    formatPathsArrayToIgnore,
    setConfigDefaults
};
<commit_msg>Removed extra empty object in setConfigDefaults method.<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

const DEFAULT_CONFIG_IGNORE_PATHS = [];
const DEFAULT_CONFIG_PARSER = 'dox';
const DEFAULT_CONFIG_PLUGIN = 'markdown';

/**
 * Finds package.json file from either the directory the script was called from or a supplied path.
 *
 *     console.log(findPackageFileInPath());
 *     console.log(findPackageFileInPath('./package.json'));
 *     console.log(findPackageFileInPath('~/git/github/doxdox/'));
 *
 * @param {String} [input] Directory or file.
 * @return {String} Path to package.json file.
 * @public
 */

const findPackageFileInPath = input => {

    if (!input) {

        input = process.cwd();

    }

    try {

        const stat = fs.statSync(input);

        if (stat.isFile()) {

            return path.resolve(path.join(path.dirname(input), '/package.json'));

        } else if (stat.isDirectory()) {

            return path.resolve(path.join(input, '/package.json'));

        }

    } catch (err) {

        process.stderr.write(`${err.toString()}\n`);

    }

    return null;

};

/**
 * Format an array of directories and/or files to be ignored by globby by adding a "!" at the beginning of each path.
 *
 *     console.log(formatPathsArrayToIgnore(['./src']));
 *
 * @param {Array} paths Array of directories and/or files.
 * @return {Array} Modified array of directories and/or files.
 * @public
 */

const formatPathsArrayToIgnore = paths =>
    paths.map(path => `!${path.replace(/^!/, '')}`);

/**
 * Sets default configuration values.
 *
 *     console.log(setConfigDefaults({}));
 *
 * @param {Object} config Custom configuration object.
 * @return {Object} Modified configuration object.
 * @public
 */

const setConfigDefaults = config => Object.assign({
    'ignore': DEFAULT_CONFIG_IGNORE_PATHS,
    'parser': DEFAULT_CONFIG_PARSER,
    'plugin': DEFAULT_CONFIG_PLUGIN
}, config);

module.exports = {
    findPackageFileInPath,
    formatPathsArrayToIgnore,
    setConfigDefaults
};
<|endoftext|>


<commit_before>/*
 * node-htmlprocessor
 * https://github.com/dciccale/node-htmlprocessor
 *
 * Copyright (c) 2013-2014 Denis Ciccale (@tdecs)
 * Licensed under the MIT license.
 * https://github.com/dciccale/node-htmlprocessor/blob/master/LICENSE-MIT
 */

'use strict';

var path = require('path');
var fs = require('fs');
var _ = require('lodash');

var utils = module.exports = {};

var escapeRegExp = function (str) {
  return str.replace(/([.?*+\^=!:$\[\]\\(){}|\-])/g, '\\$1');
};

utils.blockToRegExp = function (blockLine) {
  var escaped = escapeRegExp(blockLine);
  return new RegExp(escaped.replace(/^\s*|[\r\n]+\s*/g, '\\s*').replace(/\n{1}$/g, '\\n'));
};

utils.template = function (tmpl, data, options) {
  return _.template(tmpl, data, options);
};

utils.read = function (filepath, encoding) {
  var contents;
  if (encoding === null) {
    contents = fs.readFileSync(filepath);
  } else {
    contents = fs.readFileSync(filepath, 'utf-8');
  }
  return contents;
};

utils.exists = function (filepath) {
  return filepath && fs.existsSync(filepath);
};

// cortesy of grunt
utils.mkdir = function (dirpath, mode) {
  // Set directory mode in a strict-mode-friendly way.
  if (mode == null) {
    mode = parseInt('0777', 8) & (~process.umask());
  }
  dirpath.split(/[\/\\]/g).reduce(function(parts, part) {
    parts += part + '/';
    var subpath = path.resolve(parts);
    if (!utils.exists(subpath)) {
      try {
        fs.mkdirSync(subpath, mode);
      } catch (e) {
        throw util.error('Unable to create directory "' + subpath + '" (Error code: ' + e.code + ').', e);
      }
    }
    return parts;
  }, '');
};

utils._ = _;
<commit_msg>remove unnecessary template wrap function<commit_after>/*
 * node-htmlprocessor
 * https://github.com/dciccale/node-htmlprocessor
 *
 * Copyright (c) 2013-2014 Denis Ciccale (@tdecs)
 * Licensed under the MIT license.
 * https://github.com/dciccale/node-htmlprocessor/blob/master/LICENSE-MIT
 */

'use strict';

var path = require('path');
var fs = require('fs');
var _ = require('lodash');

var utils = module.exports = {};

var escapeRegExp = function (str) {
  return str.replace(/([.?*+\^=!:$\[\]\\(){}|\-])/g, '\\$1');
};

utils.blockToRegExp = function (blockLine) {
  var escaped = escapeRegExp(blockLine);
  return new RegExp(escaped.replace(/^\s*|[\r\n]+\s*/g, '\\s*').replace(/\n{1}$/g, '\\n'));
};

utils.read = function (filepath, encoding) {
  var contents;
  if (encoding === null) {
    contents = fs.readFileSync(filepath);
  } else {
    contents = fs.readFileSync(filepath, 'utf-8');
  }
  return contents;
};

utils.exists = function (filepath) {
  return filepath && fs.existsSync(filepath);
};

// cortesy of grunt
utils.mkdir = function (dirpath, mode) {
  // Set directory mode in a strict-mode-friendly way.
  if (mode == null) {
    mode = parseInt('0777', 8) & (~process.umask());
  }
  dirpath.split(/[\/\\]/g).reduce(function(parts, part) {
    parts += part + '/';
    var subpath = path.resolve(parts);
    if (!utils.exists(subpath)) {
      try {
        fs.mkdirSync(subpath, mode);
      } catch (e) {
        throw util.error('Unable to create directory "' + subpath + '" (Error code: ' + e.code + ').', e);
      }
    }
    return parts;
  }, '');
};

utils._ = _;
<|endoftext|>


<commit_before>"use strict";
const uniq = require('lodash.uniq');
const path = require('path');

class ModClean_Utils {
    initPatterns(opts) {
        let patDefs = opts.patterns,
            patterns = [],
            ignore = [];

        if(!Array.isArray(patDefs)) patDefs = [patDefs];

        patDefs.forEach((def) => {
            def = def.split(':');
            let mod = def[0],
                name = def[1],
                loader = this._loadPatterns(mod),
                results;

            mod = loader.module;
            results = loader.patterns;

            let all = Object.keys(results).filter(function(val) {
                return val[0] !== '$';
            });

            if(!name) {
                if(results.$default) name = results.$default;
                else name = all[0];
            }

            if(name === '*') name = all;

            let rules = Array.isArray(name)? name : name.split(',');

            rules.forEach(function(rule) {
                if(!results.hasOwnProperty(rule)) throw new Error(`Module "${mod}" does not contain rule "${rule}"`);
                let obj = results[rule];

                if(Array.isArray(obj)) return patterns = patterns.concat(obj);

                if(typeof obj === 'object') {
                    if(obj.hasOwnProperty('patterns')) patterns = patterns.concat(obj.patterns);
                    if(obj.hasOwnProperty('ignore')) ignore = ignore.concat(obj.ignore);
                }
            });
        });

        let addlPats = opts.additionalPatterns,
            addlIgnore = opts.ignorePatterns;

        if(Array.isArray(addlPats) && addlPats.length) patterns = patterns.concat(addlPats);
        if(Array.isArray(addlIgnore) && addlIgnore.length) ignore = ignore.concat(addlIgnore);

        patterns = uniq(patterns);
        ignore = uniq(ignore);

        if(!patterns.length) throw new Error('No patterns have been loaded, nothing to check against');

        return {
            allow: patterns,
            ignore
        };
    }

    _loadPatterns(module, def) {
        let patterns;

        if(module.indexOf('/') !== -1) {
            let ext = path.extname(module);
            if(!path.isAbsolute(module)) module = path.resolve(process.cwd(), module);

            if(ext === '.js' || ext === '.json') patterns = require(module);
            else throw new Error(`Invalid pattern module "${def}" provided`);
        } else {
            if(module.match(/modclean\-patterns\-/) === null) module = 'modclean-patterns-' + module;

            try {
                patterns = require(module);
            } catch(e) {
                throw new Error(`Unable to find patterns plugin "${module}", is it installed?`);
            }
        }

        if(patterns === null || typeof patterns !== 'object')
            throw new Error(`Patterns "${module}" did not return an object`);

        return {
            module,
            patterns
        };
    }
}

module.exports = ModClean_Utils;
<commit_msg>Added doc blocks<commit_after>"use strict";
const uniq = require('lodash.uniq');
const path = require('path');

class ModClean_Utils {
    /**
     * Initializes patterns based on the configuration
     * @param  {Object} opts Options object for ModClean
     * @return {Object}      The compiled and loaded patterns
     */
    initPatterns(opts) {
        let patDefs = opts.patterns,
            patterns = [],
            ignore = [];

        if(!Array.isArray(patDefs)) patDefs = [patDefs];

        patDefs.forEach((def) => {
            def = def.split(':');
            let mod = def[0],
                name = def[1],
                loader = this._loadPatterns(mod),
                results;

            mod = loader.module;
            results = loader.patterns;

            let all = Object.keys(results).filter(function(val) {
                return val[0] !== '$';
            });

            if(!name) {
                if(results.$default) name = results.$default;
                else name = all[0];
            }

            if(name === '*') name = all;

            let rules = Array.isArray(name)? name : name.split(',');

            rules.forEach(function(rule) {
                if(!results.hasOwnProperty(rule)) throw new Error(`Module "${mod}" does not contain rule "${rule}"`);
                let obj = results[rule];

                if(Array.isArray(obj)) return patterns = patterns.concat(obj);

                if(typeof obj === 'object') {
                    if(obj.hasOwnProperty('patterns')) patterns = patterns.concat(obj.patterns);
                    if(obj.hasOwnProperty('ignore')) ignore = ignore.concat(obj.ignore);
                }
            });
        });

        let addlPats = opts.additionalPatterns,
            addlIgnore = opts.ignorePatterns;

        if(Array.isArray(addlPats) && addlPats.length) patterns = patterns.concat(addlPats);
        if(Array.isArray(addlIgnore) && addlIgnore.length) ignore = ignore.concat(addlIgnore);

        patterns = uniq(patterns);
        ignore = uniq(ignore);

        if(!patterns.length) throw new Error('No patterns have been loaded, nothing to check against');

        return {
            allow: patterns,
            ignore
        };
    }

    /**
     * Parses pattern configuration item and attempts to load it
     * @param  {String} module Module name or path to load
     * @param  {String} def    Raw definition provided in the configuration
     * @return {Object}        Object containing the found module name and the loaded patterns
     */
    _loadPatterns(module, def) {
        let patterns;

        if(module.indexOf('/') !== -1) {
            let ext = path.extname(module);
            if(!path.isAbsolute(module)) module = path.resolve(process.cwd(), module);

            if(ext === '.js' || ext === '.json') patterns = require(module);
            else throw new Error(`Invalid pattern module "${def}" provided`);
        } else {
            if(module.match(/modclean\-patterns\-/) === null) module = 'modclean-patterns-' + module;

            try {
                patterns = require(module);
            } catch(e) {
                throw new Error(`Unable to find patterns plugin "${module}", is it installed?`);
            }
        }

        if(patterns === null || typeof patterns !== 'object')
            throw new Error(`Patterns "${module}" did not return an object`);

        return {
            module,
            patterns
        };
    }
}

module.exports = ModClean_Utils;
<|endoftext|>


<commit_before>module.exports.extend = function(constructor, parent) {
  constructor.prototype = Object.create(parent.prototype);
  constructor.prototype.constructor = constructor;
};

var utils = {};

utils.extend = function(constructor, parent) {
  constructor.prototype = Object.create(parent.prototype);
  constructor.prototype.constructor = constructor;
};

utils.toRadians = function(degrees) {
  return degrees * Math.PI / 180;
};

utils.toDegrees = function(radians) {
  return radians * 180 / Math.PI;
};

utils.options = function(options, object) {
    if (typeof options !== 'object') {
        options = {};
    }
    if (options.value !== undefined) {
        throw new TypeError('Could not create options object.');
    }
    options.value = function (name, value) {
        if (options[name] === undefined) {
            if (object) {
                object[name] = value;
            }
            return value;
        }
        if (object) {
            object[name] = options[name];
        }
        return options[name];
    };
    return options;
};

module.exports.Utils = utils;<commit_msg>Added license.<commit_after>/*The MIT License (MIT)

 Copyright (c) 2015 Jens Malmborg

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.*/

module.exports.extend = function(constructor, parent) {
  constructor.prototype = Object.create(parent.prototype);
  constructor.prototype.constructor = constructor;
};

var utils = {};

utils.extend = function(constructor, parent) {
  constructor.prototype = Object.create(parent.prototype);
  constructor.prototype.constructor = constructor;
};

utils.toRadians = function(degrees) {
  return degrees * Math.PI / 180;
};

utils.toDegrees = function(radians) {
  return radians * 180 / Math.PI;
};

utils.options = function(options, object) {
    if (typeof options !== 'object') {
        options = {};
    }
    if (options.value !== undefined) {
        throw new TypeError('Could not create options object.');
    }
    options.value = function (name, value) {
        if (options[name] === undefined) {
            if (object) {
                object[name] = value;
            }
            return value;
        }
        if (object) {
            object[name] = options[name];
        }
        return options[name];
    };
    return options;
};

module.exports.Utils = utils;<|endoftext|>


<commit_before>const fs = require('fs')
const { promisify } = require('util')
const { isArray } = Array

function fileExists (path) {
  return new Promise(resolve => {
    promisify(fs.access)(path)
      .then(() => resolve(true))
      .catch(() => resolve(false))
  })
}

function isString (thing) {
  return typeof thing === 'string'
}

function isObject (thing) {
  return typeof thing === 'object' && !isArray(thing)
}

function options2Args (options) {
  let result = []

  if (isObject(options)) {
    Object.keys(options)
      // ensure `files` is first
      .sort((a, b) => {
        if (a === 'files') return -1
        if (b === 'files') return 1
        else return 0
      })
      .forEach(key => {
        const value = options[key]
        let arg

        if (key === 'files' && isArray(value)) {
          arg = value
        } else if (value === true) {
          arg = `--${key}`
        } else if (value === false) {
          arg = `--no-${key}`
        } else if (value) {
          arg = `--${key}=${value}`
        }

        if (arg) result = result.concat(arg)
      })
  }

  return result
}

module.exports = {
  fileExists,
  isArray,
  isString,
  isObject,
  options2Args
}
<commit_msg>minor tweak<commit_after>const fs = require('fs')
const { promisify } = require('util')
const { isArray } = Array

function fileExists (path) {
  return new Promise(resolve => {
    promisify(fs.access)(path)
      .then(() => resolve(true))
      .catch(() => resolve(false))
  })
}

function isString (thing) {
  return typeof thing === 'string'
}

function isObject (thing) {
  return typeof thing === 'object' && !isArray(thing)
}

function options2Args (options) {
  let result = []

  if (isObject(options)) {
    Object.keys(options)
      // ensure `files` is first
      .sort((a, b) => {
        if (a === 'files') return -1
        else if (b === 'files') return 1
        else return 0
      })
      .forEach(key => {
        const value = options[key]
        let arg

        if (key === 'files' && isArray(value)) {
          arg = value
        } else if (value === true) {
          arg = `--${key}`
        } else if (value === false) {
          arg = `--no-${key}`
        } else if (value) {
          arg = `--${key}=${value}`
        }

        if (arg) result = result.concat(arg)
      })
  }

  return result
}

module.exports = {
  fileExists,
  isArray,
  isString,
  isObject,
  options2Args
}
<|endoftext|>