<commit_before>'use strict'

var http = require('http')
var util = require('util')
var events = require('events')
var uuid = require('node-uuid')
var OpbeatHttpClient = require('opbeat-http-client')
var ReleaseTracker = require('opbeat-release-tracker')
var config = require('./lib/config')
var asyncState = require('./lib/async-state')
var hooks = require('./lib/hooks')
var parsers = require('./lib/parsers')
var request = require('./lib/request')
var connect = require('./lib/middleware/connect')
var Instrumentation = require('./lib/instrumentation')

var userAgent = 'opbeat-nodejs/' + require('./package').version

var Opbeat = module.exports = function (opts) {
  if (!(this instanceof Opbeat)) return new Opbeat(opts)

  events.EventEmitter.call(this)

  opts = config(opts)
  this.appId = opts.appId
  this.organizationId = opts.organizationId
  this.secretToken = opts.secretToken
  this.active = opts.active
  this.clientLogLevel = opts.clientLogLevel
  this.logger = opts.logger
  this.hostname = opts.hostname
  this.stackTraceLimit = opts.stackTraceLimit
  this.captureExceptions = opts.captureExceptions
  this.exceptionLogLevel = opts.exceptionLogLevel
  this.filter = opts.filter
  this._apiHost = opts._apiHost
  this._ff_captureFrame = opts._ff_captureFrame
  this._ff_instrument = opts._ff_instrument

  connect = connect.bind(this)
  this.middleware = { connect: connect, express: connect }

  this._instrumentation = new Instrumentation(this)

  if (!this.active) {
    this.logger.info('Opbeat logging is disabled for now')
  } else if (!this.appId || !this.organizationId || !this.secretToken) {
    this.logger.info('[WARNING] Opbeat logging is disabled. To enable, specify organization id, app id and secret token')
    this.active = false
  } else {
    this._start()
  }
}

util.inherits(Opbeat, events.EventEmitter)

Opbeat.prototype._start = function () {
  var client = this

  hooks(this) // hook into node for enhanced error tracking

  this._httpClient = OpbeatHttpClient({
    appId: this.appId,
    organizationId: this.organizationId,
    secretToken: this.secretToken,
    userAgent: userAgent,
    _apiHost: this._apiHost
  })

  Error.stackTraceLimit = this.stackTraceLimit
  if (this.captureExceptions) this.handleUncaughtExceptions()

  this.on('error', this._internalErrorLogger)
  this.on('logged', function (url, uuid) {
    client.logger.info('[%s] Opbeat logged error successfully at %s', uuid, url)
  })
}

Opbeat.prototype.captureError = function (err, data, cb) {
  var client = this
  var captureTime = new Date()

  if (typeof data === 'function') {
    cb = data
    data = {}
  } else if (!data) {
    data = {}
  } else if (data.request instanceof http.IncomingMessage) {
    data.http = parsers.parseRequest(data.request)
  }
  delete data.request

  if (!data.http && asyncState.req) data.http = parsers.parseRequest(asyncState.req)

  var level = this.exceptionLogLevel || 'error'
  level = level === 'warning' ? 'warn' : level

  var errUUID = data.extra && data.extra.uuid || uuid.v4()

  if (!util.isError(err)) {
    var isMessage = true
    var customCulprit = 'culprit' in data
    parsers.parseMessage(err, data)
    this.logger[level]('[%s]', errUUID, data.message)
    err = new Error(data.message)
  } else if (this._ff_captureFrame && !err.uncaught) {
    var captureFrameError = new Error()
  }

  if (!isMessage) {
    client.logger.info('[%s] logging error with Opbeat:', errUUID)
    client.logger[level](err.stack)
  }

  parsers.parseError(err, data, function (data) {
    if (isMessage) {
      // Messages shouldn't have an exception and the algorithm for finding the
      // culprit might show the Opbeat client and we don't want that
      delete data.exception
      if (!customCulprit) delete data.culprit
      data.stacktrace.frames.shift()
    }

    var done = function () {
      data.stacktrace.frames.reverse() // opbeat expects frames in reverse order
      data.machine = { hostname: client.hostname }
      data.extra = data.extra || {}
      data.extra.node = process.version
      if (!data.extra.uuid) data.extra.uuid = errUUID
      data.timestamp = captureTime.toISOString()

      if (client.filter) data = client.filter(err, data)
      if (client.active) request.error(client, data, cb)
    }

    if (captureFrameError && !data.stacktrace.frames.some(function (frame) { return frame.in_app })) {
      // prepare to add a top frame to the stack trace specifying the location
      // where captureError was called from. This can make it easier to debug
      // async stack traces.
      parsers.parseError(captureFrameError, {}, function (result) {
        // ignore the first frame as it will be the opbeat module
        data.stacktrace.frames.unshift(result.stacktrace.frames[1])
        done()
      })
    } else {
      done()
    }
  })
}

// The optional callback will be called with the error object after the
// error have been logged to Opbeat. If no callback have been provided
// we will automatically terminate the process, so if you provide a
// callback you must remember to terminate the process manually.
Opbeat.prototype.handleUncaughtExceptions = function (cb) {
  var client = this

  if (this._uncaughtExceptionListener) process.removeListener('uncaughtException', this._uncaughtExceptionListener)

  this._uncaughtExceptionListener = function (err) {
    var data = {
      extra: { uuid: uuid.v4() },
      level: client.exceptionLogLevel
    }

    client.logger.debug('[%s] Opbeat caught unhandled exception', data.extra.uuid)

    // Since we exit the node-process we cannot guarantee that the
    // listeners will be called, so to ensure a uniform result,
    // we'll remove all event listeners if an uncaught exception is
    // found
    client.removeAllListeners()
    // But make sure emitted errors doesn't cause yet another uncaught
    // exception
    client.on('error', client._internalErrorLogger)

    err.uncaught = true

    client.captureError(err, data, function (opbeatErr, url) {
      if (opbeatErr) {
        client.logger.info('[%s] Could not notify Opbeat!', data.extra.uuid)
        client.logger.error(opbeatErr.stack)
      } else {
        client.logger.info('[%s] Opbeat logged error successfully at %s', data.extra.uuid, url)
      }
      cb ? cb(err, url) : process.exit(1)
    })
  }

  process.on('uncaughtException', this._uncaughtExceptionListener)
}

Opbeat.prototype.trackRelease = function (data, cb) {
  if (data.path) {
    this.logger.warn('Detected use of deprecated path option to trackRelease function!')
    if (!data.cwd) data.cwd = data.path
  }
  if (!this._releaseTracker) this._releaseTracker = ReleaseTracker(this._httpClient)
  var client = this
  this._releaseTracker(data, function (err) {
    if (cb) cb(err)
    if (err) client.emit('error', err)
  })
}

Opbeat.prototype.startTransaction = function (name, type, result) {
  return this._instrumentation.startTransaction(name, type, result)
}

Opbeat.prototype.setTransactionName = function (name) {
  if (!asyncState.trans) {
    this.logger.warn('no transaction found!')
    return
  }
  this.logger.trace('[%s] setting transaction name: %s', asyncState.trans._uuid, name)
  asyncState.trans.name = name
}

Opbeat.prototype.trans = function () {
  return asyncState.trans
}

Opbeat.prototype.trackDeployment = Opbeat.prototype.trackRelease

Opbeat.prototype._internalErrorLogger = function (err, uuid) {
  if (uuid) this.logger.info('[%s] Could not notify Opbeat!', uuid)
  else this.logger.info('Could not notify Opbeat!')
  this.logger.error(err.stack)
}
<commit_msg>Don't expose ended transactions<commit_after>'use strict'

var http = require('http')
var util = require('util')
var events = require('events')
var uuid = require('node-uuid')
var OpbeatHttpClient = require('opbeat-http-client')
var ReleaseTracker = require('opbeat-release-tracker')
var config = require('./lib/config')
var asyncState = require('./lib/async-state')
var hooks = require('./lib/hooks')
var parsers = require('./lib/parsers')
var request = require('./lib/request')
var connect = require('./lib/middleware/connect')
var Instrumentation = require('./lib/instrumentation')

var userAgent = 'opbeat-nodejs/' + require('./package').version

var Opbeat = module.exports = function (opts) {
  if (!(this instanceof Opbeat)) return new Opbeat(opts)

  events.EventEmitter.call(this)

  opts = config(opts)
  this.appId = opts.appId
  this.organizationId = opts.organizationId
  this.secretToken = opts.secretToken
  this.active = opts.active
  this.clientLogLevel = opts.clientLogLevel
  this.logger = opts.logger
  this.hostname = opts.hostname
  this.stackTraceLimit = opts.stackTraceLimit
  this.captureExceptions = opts.captureExceptions
  this.exceptionLogLevel = opts.exceptionLogLevel
  this.filter = opts.filter
  this._apiHost = opts._apiHost
  this._ff_captureFrame = opts._ff_captureFrame
  this._ff_instrument = opts._ff_instrument

  connect = connect.bind(this)
  this.middleware = { connect: connect, express: connect }

  this._instrumentation = new Instrumentation(this)

  if (!this.active) {
    this.logger.info('Opbeat logging is disabled for now')
  } else if (!this.appId || !this.organizationId || !this.secretToken) {
    this.logger.info('[WARNING] Opbeat logging is disabled. To enable, specify organization id, app id and secret token')
    this.active = false
  } else {
    this._start()
  }
}

util.inherits(Opbeat, events.EventEmitter)

Opbeat.prototype._start = function () {
  var client = this

  hooks(this) // hook into node for enhanced error tracking

  this._httpClient = OpbeatHttpClient({
    appId: this.appId,
    organizationId: this.organizationId,
    secretToken: this.secretToken,
    userAgent: userAgent,
    _apiHost: this._apiHost
  })

  Error.stackTraceLimit = this.stackTraceLimit
  if (this.captureExceptions) this.handleUncaughtExceptions()

  this.on('error', this._internalErrorLogger)
  this.on('logged', function (url, uuid) {
    client.logger.info('[%s] Opbeat logged error successfully at %s', uuid, url)
  })
}

Opbeat.prototype.captureError = function (err, data, cb) {
  var client = this
  var captureTime = new Date()

  if (typeof data === 'function') {
    cb = data
    data = {}
  } else if (!data) {
    data = {}
  } else if (data.request instanceof http.IncomingMessage) {
    data.http = parsers.parseRequest(data.request)
  }
  delete data.request

  if (!data.http && asyncState.req) data.http = parsers.parseRequest(asyncState.req)

  var level = this.exceptionLogLevel || 'error'
  level = level === 'warning' ? 'warn' : level

  var errUUID = data.extra && data.extra.uuid || uuid.v4()

  if (!util.isError(err)) {
    var isMessage = true
    var customCulprit = 'culprit' in data
    parsers.parseMessage(err, data)
    this.logger[level]('[%s]', errUUID, data.message)
    err = new Error(data.message)
  } else if (this._ff_captureFrame && !err.uncaught) {
    var captureFrameError = new Error()
  }

  if (!isMessage) {
    client.logger.info('[%s] logging error with Opbeat:', errUUID)
    client.logger[level](err.stack)
  }

  parsers.parseError(err, data, function (data) {
    if (isMessage) {
      // Messages shouldn't have an exception and the algorithm for finding the
      // culprit might show the Opbeat client and we don't want that
      delete data.exception
      if (!customCulprit) delete data.culprit
      data.stacktrace.frames.shift()
    }

    var done = function () {
      data.stacktrace.frames.reverse() // opbeat expects frames in reverse order
      data.machine = { hostname: client.hostname }
      data.extra = data.extra || {}
      data.extra.node = process.version
      if (!data.extra.uuid) data.extra.uuid = errUUID
      data.timestamp = captureTime.toISOString()

      if (client.filter) data = client.filter(err, data)
      if (client.active) request.error(client, data, cb)
    }

    if (captureFrameError && !data.stacktrace.frames.some(function (frame) { return frame.in_app })) {
      // prepare to add a top frame to the stack trace specifying the location
      // where captureError was called from. This can make it easier to debug
      // async stack traces.
      parsers.parseError(captureFrameError, {}, function (result) {
        // ignore the first frame as it will be the opbeat module
        data.stacktrace.frames.unshift(result.stacktrace.frames[1])
        done()
      })
    } else {
      done()
    }
  })
}

// The optional callback will be called with the error object after the
// error have been logged to Opbeat. If no callback have been provided
// we will automatically terminate the process, so if you provide a
// callback you must remember to terminate the process manually.
Opbeat.prototype.handleUncaughtExceptions = function (cb) {
  var client = this

  if (this._uncaughtExceptionListener) process.removeListener('uncaughtException', this._uncaughtExceptionListener)

  this._uncaughtExceptionListener = function (err) {
    var data = {
      extra: { uuid: uuid.v4() },
      level: client.exceptionLogLevel
    }

    client.logger.debug('[%s] Opbeat caught unhandled exception', data.extra.uuid)

    // Since we exit the node-process we cannot guarantee that the
    // listeners will be called, so to ensure a uniform result,
    // we'll remove all event listeners if an uncaught exception is
    // found
    client.removeAllListeners()
    // But make sure emitted errors doesn't cause yet another uncaught
    // exception
    client.on('error', client._internalErrorLogger)

    err.uncaught = true

    client.captureError(err, data, function (opbeatErr, url) {
      if (opbeatErr) {
        client.logger.info('[%s] Could not notify Opbeat!', data.extra.uuid)
        client.logger.error(opbeatErr.stack)
      } else {
        client.logger.info('[%s] Opbeat logged error successfully at %s', data.extra.uuid, url)
      }
      cb ? cb(err, url) : process.exit(1)
    })
  }

  process.on('uncaughtException', this._uncaughtExceptionListener)
}

Opbeat.prototype.trackRelease = function (data, cb) {
  if (data.path) {
    this.logger.warn('Detected use of deprecated path option to trackRelease function!')
    if (!data.cwd) data.cwd = data.path
  }
  if (!this._releaseTracker) this._releaseTracker = ReleaseTracker(this._httpClient)
  var client = this
  this._releaseTracker(data, function (err) {
    if (cb) cb(err)
    if (err) client.emit('error', err)
  })
}

Opbeat.prototype.startTransaction = function (name, type, result) {
  return this._instrumentation.startTransaction(name, type, result)
}

Opbeat.prototype.setTransactionName = function (name) {
  if (!asyncState.trans) {
    this.logger.warn('no transaction found!')
    return
  }
  this.logger.trace('[%s] setting transaction name: %s', asyncState.trans._uuid, name)
  asyncState.trans.name = name
}

Opbeat.prototype.trans = function () {
  var trans = asyncState.trans
  if (trans && !trans.ended) return trans
}

Opbeat.prototype.trackDeployment = Opbeat.prototype.trackRelease

Opbeat.prototype._internalErrorLogger = function (err, uuid) {
  if (uuid) this.logger.info('[%s] Could not notify Opbeat!', uuid)
  else this.logger.info('Could not notify Opbeat!')
  this.logger.error(err.stack)
}
<|endoftext|>


<commit_before>require('babel/register');

var React = require('react');
var ReactDOMServer = require('react-dom/server');
var App = require('./src/ui/grid');

module.exports = ReactDOMServer.renderToStaticMarkup(React.createElement(App));
<commit_msg>modularize package<commit_after>require('babel/register');

var React = require('react');
var ReactDOMServer = require('react-dom/server');
var ContributionsGrid = require('./src/ui/grid');
var CommitsFetcher = require('./src/jobs/commits-fetcher');
var TimelineGenerator = require('./src/data/timeline-generator');

var bitbucketCommitsFetcher = require('./src/data/bitbucket-commits-fetcher');

module.exports = {
    getStaticMarkup: function() {ReactDOMServer.renderToStaticMarkup(React.createElement(ContributionsGrid));},
    fetchContributions: CommitsFetcher.run,
    getContributionsTimeline: TimelineGenerator.generateCommitsTimeline
};
<|endoftext|>


<commit_before>// inspired by https://github.com/NYTimes/Emphasis

var crypto = require('crypto')
  , sbd = require('sbd')
  , uuid = require('uuid')
  , levenshtein = require('fast-levenshtein');

var citeable = exports.citeable = ['P', 'LI', 'DD', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'FIGCAPTION', 'CAPTION', 'ASIDE'];

/**
 * From a block element, generate a Key
 * - Break block element text content into Sentences
 * - Take first and last Sentences
 * - Sometimes the same. Thats ok.
 * - First character from the first three words of each sentence
 * - Each 6 char key refers to specific block element
 */
var createKey = exports.createKey = function($el) {
  var key = '';
  var len = 6;
  var txt = ($el.textContent || '').replace(/[^a-z\. ]+/gi, '').trim();

  if (txt && txt.length>1) {
    var lines = sbd.sentences(txt)
          .map(function(x) {return x.trim();})
          .filter(function(x) {return x;});

    if (lines.length) {
      var first = lines[0].match(/\S+/g).slice(0, (len/2));
      var last = lines[lines.length-1].match(/\S+/g).slice(0, (len/2));
      var k = first.concat(last);

      var max = (k.length > len) ? len : k.length;

      for (var i=0; i < max; i++) {
        key += k[i].substring(0, 1);
      }
    }
  }

  return key;
};

var createHash = exports.createHash = function($el, algorithm) {
  algorithm = algorithm || 'sha1';
  return crypto.createHash(algorithm).update($el.textContent.trim(), 'utf8').digest('hex'); //TODO textContent.replace(/\s+/g, ' ') ??
};

var getScope = exports.getScope = function(range) {
  var $scope = range.commonAncestorContainer;
  if ($scope.nodeType === Node.TEXT_NODE) {
    $scope = $scope.parentElement; //get closest Element
  };

  // TODO generalize to list of supported block elements
  // get closest citeable element
  while (!~citeable.indexOf($scope.tagName)) {
    $scope = $scope.parentElement;
    if ($scope.tagName === 'HTML') {
      return;
    }
  }
  return $scope;
};

var getOffsets = exports.getOffsets = function(range, $scope) {

  var startTextNode, endTextNode;
  if (range.startContainer.nodeType === Node.TEXT_NODE) {
    startTextNode = range.startContainer;
  } else {
    for (var i=0; i < range.startContainer.childNodes.length; i++) {
      if (range.startContainer.childNodes[i].nodeType === Node.TEXT_NODE) {
        startTextNode = range.startContainer.childNodes[i];
        break;
      }
    }
  }

  if (range.endContainer.nodeType === Node.TEXT_NODE) {
    endTextNode = range.endContainer;
  } else {
    for (var j=0; j < range.endContainer.childNodes.length; j++) {
      if (range.endContainer.childNodes[j].nodeType === Node.TEXT_NODE) {
        endTextNode = range.endContainer.childNodes[j];
        break;
      }
    }
  }

  var node, indStartTextNode, indEndTextNode, ind=0, textNodes = [];
  var it = document.createNodeIterator($scope, NodeFilter.SHOW_TEXT);
  while (node = it.nextNode()) {
    textNodes.push(node);
    if (node === startTextNode) {
      indStartTextNode = ind;
    }
    if (node === endTextNode) {
      indEndTextNode = ind;
      break;
    }
    ind++;
  }

  var startOffset = textNodes.slice(0, indStartTextNode).reduce(function(a, b){
    return a + b.textContent.trim().length;
  }, 0);

  if (range.startOffset !== undefined) {
    startOffset += range.startOffset - (startTextNode.textContent.length - startTextNode.textContent.replace(/^\s+/, '').length); //we substract the effect of having trimmed the textContent
  }

  var endOffset = textNodes.slice(0, indEndTextNode).reduce(function(a, b){
    return a + b.textContent.trim().length;
  }, 0);
  if (range.endOffset !== undefined) {
    endOffset += range.endOffset - (endTextNode.textContent.length - endTextNode.textContent.replace(/^\s+/, '').length); //we substract the effect of having trimmed the textContent;
  }

  return {startOffset: startOffset, endOffset: endOffset};
};

var serializeRange = exports.serializeRange = function(range, $scope) {
  var $scope = $scope || getScope(range);
  if (!$scope) return;

  var offsets = getOffsets(range, $scope);

  return {
    $scope: $scope,
    sha1: createHash($scope),
    key: createKey($scope),
    startOffset: offsets.startOffset,
    endOffset: offsets.endOffset,
    text: range.toString().trim()
  };
};

//key:start-end
exports.serializeSelection = function() {
  var selection = window.getSelection();
  var range;
  if (!selection.isCollapsed) {
    range = selection.getRangeAt(0);
  } else {
    return;
  }

  return serializeRange(range);
};

exports.rangeFromOffsets = function($scope, startOffset, endOffset) {
  var node;
  var it = document.createNodeIterator($scope, NodeFilter.SHOW_TEXT);
  var acc = 0;
  var startNode, endNode, relStartOffset, relEndOffset;
  var textContent;

  while (node = it.nextNode()) {
    textContent = node.textContent.trim();
    if (relStartOffset === undefined && ((acc + textContent.length) >= startOffset)) {
      startNode = node;
      relStartOffset = startOffset - acc + (startNode.textContent.length - startNode.textContent.replace(/^\s+/, '').length); //we add back the effect of having trimmed the textContent;
    }
    if (relEndOffset === undefined && ((acc + textContent.length) >= endOffset)) {
      endNode = node;
      relEndOffset = endOffset-acc + (endNode.textContent.length - endNode.textContent.replace(/^\s+/, '').length); //we add back the effect of having trimmed the textContent;
      break;
    }
    acc += textContent.length;
  }

  var range = document.createRange();
  range.setStart(startNode, relStartOffset);
  range.setEnd(endNode, relEndOffset);

  return range;
};

exports.findKey = function(target, candidates) {
  var x = {index: undefined, value: undefined, lev: undefined};

  for (var i=0; i < candidates.length; i++) {
    if (target === candidates[i]) {
      return {index: i, value: candidates[i], lev: 0};
    } else { //look for 1st closest Match
      var ls = levenshtein.get(target.slice(0, 3), candidates[i].slice(0, 3));
      var le = levenshtein.get(target.slice(-3), candidates[i].slice(-3));
      var lev = ls+le;
      if (lev < 3 && ((x.lev === undefined) || (lev < x.lev)) ) {
        x.index = i;
        x.value = candidates[i];
        x.lev = lev;
      }
    }
  }

  return x;
};

exports.addIdentifiers = function($doc) {
  Array.prototype.forEach.call($doc.body.getElementsByTagName('*'), function($el) {
    $el.setAttribute('data-id', uuid.v1());
    $el.setAttribute('data-hash', createHash($el));
    if (~citeable.indexOf($el.tagName)) {
      $el.setAttribute('data-key', createKey($el));
    }
  });
  return $doc;
};
<commit_msg>add webVerse.getChildOffsets() method to extract the offsets of a child node given a parent and child node<commit_after>// inspired by https://github.com/NYTimes/Emphasis

var crypto = require('crypto')
  , sbd = require('sbd')
  , uuid = require('uuid')
  , levenshtein = require('fast-levenshtein');

var citeable = exports.citeable = ['P', 'LI', 'DD', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'FIGCAPTION', 'CAPTION', 'ASIDE'];

/**
 * From a block element, generate a Key
 * - Break block element text content into Sentences
 * - Take first and last Sentences
 * - Sometimes the same. Thats ok.
 * - First character from the first three words of each sentence
 * - Each 6 char key refers to specific block element
 */
var createKey = exports.createKey = function($el) {
  var key = '';
  var len = 6;
  var txt = ($el.textContent || '').replace(/[^a-z\. ]+/gi, '').trim();

  if (txt && txt.length>1) {
    var lines = sbd.sentences(txt)
          .map(function(x) {return x.trim();})
          .filter(function(x) {return x;});

    if (lines.length) {
      var first = lines[0].match(/\S+/g).slice(0, (len/2));
      var last = lines[lines.length-1].match(/\S+/g).slice(0, (len/2));
      var k = first.concat(last);

      var max = (k.length > len) ? len : k.length;

      for (var i=0; i < max; i++) {
        key += k[i].substring(0, 1);
      }
    }
  }

  return key;
};

var createHash = exports.createHash = function($el, algorithm) {
  algorithm = algorithm || 'sha1';
  return crypto.createHash(algorithm).update($el.textContent.trim(), 'utf8').digest('hex'); //TODO textContent.replace(/\s+/g, ' ') ??
};

var getScope = exports.getScope = function(range) {
  var $scope = range.commonAncestorContainer;
  if ($scope.nodeType === Node.TEXT_NODE) {
    $scope = $scope.parentElement; //get closest Element
  };

  // TODO generalize to list of supported block elements
  // get closest citeable element
  while (!~citeable.indexOf($scope.tagName)) {
    $scope = $scope.parentElement;
    if ($scope.tagName === 'HTML') {
      return;
    }
  }
  return $scope;
};

var getOffsets = exports.getOffsets = function(range, $scope) {

  var startTextNode, endTextNode;
  if (range.startContainer.nodeType === Node.TEXT_NODE) {
    startTextNode = range.startContainer;
  } else {
    for (var i=0; i < range.startContainer.childNodes.length; i++) {
      if (range.startContainer.childNodes[i].nodeType === Node.TEXT_NODE) {
        startTextNode = range.startContainer.childNodes[i];
        break;
      }
    }
  }

  if (range.endContainer.nodeType === Node.TEXT_NODE) {
    endTextNode = range.endContainer;
  } else {
    for (var j=0; j < range.endContainer.childNodes.length; j++) {
      if (range.endContainer.childNodes[j].nodeType === Node.TEXT_NODE) {
        endTextNode = range.endContainer.childNodes[j];
        break;
      }
    }
  }

  var node, indStartTextNode, indEndTextNode, ind=0, textNodes = [];
  var it = document.createNodeIterator($scope, NodeFilter.SHOW_TEXT);
  while (node = it.nextNode()) {
    textNodes.push(node);
    if (node === startTextNode) {
      indStartTextNode = ind;
    }
    if (node === endTextNode) {
      indEndTextNode = ind;
      break;
    }
    ind++;
  }

  var startOffset = textNodes.slice(0, indStartTextNode).reduce(function(a, b){
    return a + b.textContent.trim().length;
  }, 0);

  if (range.startOffset !== undefined) {
    startOffset += range.startOffset - (startTextNode.textContent.length - startTextNode.textContent.replace(/^\s+/, '').length); //we substract the effect of having trimmed the textContent
  }

  var endOffset = textNodes.slice(0, indEndTextNode).reduce(function(a, b){
    return a + b.textContent.trim().length;
  }, 0);
  if (range.endOffset !== undefined) {
    endOffset += range.endOffset - (endTextNode.textContent.length - endTextNode.textContent.replace(/^\s+/, '').length); //we substract the effect of having trimmed the textContent;
  }

  return {startOffset: startOffset, endOffset: endOffset};
};

var serializeRange = exports.serializeRange = function(range, $scope) {
  var $scope = $scope || getScope(range);
  if (!$scope) return;

  var offsets = getOffsets(range, $scope);

  return {
    $scope: $scope,
    sha1: createHash($scope),
    key: createKey($scope),
    startOffset: offsets.startOffset,
    endOffset: offsets.endOffset,
    text: range.toString().trim()
  };
};

//key:start-end
exports.serializeSelection = function() {
  var selection = window.getSelection();
  var range;
  if (!selection.isCollapsed) {
    range = selection.getRangeAt(0);
  } else {
    return;
  }

  return serializeRange(range);
};

exports.rangeFromOffsets = function($scope, startOffset, endOffset) {
  var node;
  var it = document.createNodeIterator($scope, NodeFilter.SHOW_TEXT);
  var acc = 0;
  var startNode, endNode, relStartOffset, relEndOffset;
  var textContent;

  while (node = it.nextNode()) {
    textContent = node.textContent.trim();
    if (relStartOffset === undefined && ((acc + textContent.length) >= startOffset)) {
      startNode = node;
      relStartOffset = startOffset - acc + (startNode.textContent.length - startNode.textContent.replace(/^\s+/, '').length); //we add back the effect of having trimmed the textContent;
    }
    if (relEndOffset === undefined && ((acc + textContent.length) >= endOffset)) {
      endNode = node;
      relEndOffset = endOffset-acc + (endNode.textContent.length - endNode.textContent.replace(/^\s+/, '').length); //we add back the effect of having trimmed the textContent;
      break;
    }
    acc += textContent.length;
  }

  var range = document.createRange();
  range.setStart(startNode, relStartOffset);
  range.setEnd(endNode, relEndOffset);

  return range;
};

exports.findKey = function(target, candidates) {
  var x = {index: undefined, value: undefined, lev: undefined};

  for (var i=0; i < candidates.length; i++) {
    if (target === candidates[i]) {
      return {index: i, value: candidates[i], lev: 0};
    } else { //look for 1st closest Match
      var ls = levenshtein.get(target.slice(0, 3), candidates[i].slice(0, 3));
      var le = levenshtein.get(target.slice(-3), candidates[i].slice(-3));
      var lev = ls+le;
      if (lev < 3 && ((x.lev === undefined) || (lev < x.lev)) ) {
        x.index = i;
        x.value = candidates[i];
        x.lev = lev;
      }
    }
  }

  return x;
};

exports.addIdentifiers = function($doc) {
  Array.prototype.forEach.call($doc.body.getElementsByTagName('*'), function($el) {
    $el.setAttribute('data-id', uuid.v1());
    $el.setAttribute('data-hash', createHash($el));
    if (~citeable.indexOf($el.tagName)) {
      $el.setAttribute('data-key', createKey($el));
    }
  });
  return $doc;
};

exports.getChildOffsets = function($parent, $child) {
  var startTextNode;
  if ($child.nodeType === Node.TEXT_NODE) {
    startTextNode = $child;
  } else {
    for (var i = 0; i < $child.childNodes.length; i++) {
      if ($child.childNodes[i].nodeType === Node.TEXT_NODE) {
        startTextNode = $child.childNodes[i];
        break;
      }
    }
  }

  var node, indStartTextNode, ind = 0, textNodes = [];
  var it = document.createNodeIterator($parent, NodeFilter.SHOW_TEXT);
  while (node = it.nextNode()) {
    textNodes.push(node);
    if (node === startTextNode) {
      indStartTextNode = ind;
      break;
    }
    ind++;
  }

  var startOffset = textNodes.slice(0, indStartTextNode).reduce(function(a, b) {
    return a + b.textContent.trim().length;
  }, 0);

  var endOffset = textNodes.slice(0, indStartTextNode + 1).reduce(function(a, b) {
    return a + b.textContent.trim().length;
  }, 0);

  return { startOffset: startOffset, endOffset: endOffset };
};
<|endoftext|>


<commit_before>var postcss = require('postcss')

function indentResolve (str, options) {
  if (str.match(/\n(?!\n)\s*/g) === null) {
    return str
  }

  if (options.length === undefined) {
    options.lastLine = str.substr(str.lastIndexOf('\n') + 1)
    var newStr = str.substr(0, str.lastIndexOf('\n') + 1)
    options.length = Math.min.apply(Math, newStr.match(/\n(?!\n)\s*/g).filter(function (space) {
      return space.length > 2
    }).map(function (space) {
      return space.length
    }))

    if (options.length === Infinity) {
      return str
    }

    options.match = new Array(options.length).join(' ')
    str = str.replace(new RegExp(options.match, 'g'), '')
  } else {
    str = str.replace(/\n/g, '\n' + options.match)
    str = str.substr(0, str.lastIndexOf('\n') + 1) + options.lastLine
  }
  return str
}

module.exports = function (plugins, options, filterType) {
  plugins = [].concat(plugins)
  options = options || {}

  var css = postcss(plugins)

  return function posthtmlPostcss (tree) {
    var promises = []

    tree.walk(function (node) {
      var promise
      var indent = {
        length: undefined,
        match: '',
        lastLine: ''
      }

      if (node.tag === 'style' && node.content) {
        var meetsFilter = true
        if (filterType) {
          var typeAttr = (node.attrs && node.attrs.type) ? node.attrs.type.trim() : ''
          var meetsTypeAttr = filterType.test(typeAttr)
          var meetsStandardType = filterType.test('text/css') && (meetsTypeAttr || typeAttr === '')
          var meetsOtherType = !meetsStandardType && meetsTypeAttr
          meetsFilter = meetsStandardType || meetsOtherType
        }

        if (meetsFilter) {
          var styles = indentResolve([].concat(node.content).join(''), indent)
          promise = css.process(styles, options)
            .then(function (result) {
              node.content = [indentResolve(result.css, indent)]
            })

          promises.push(promise)
        }
      }

      if (node.attrs && node.attrs.style) {
        promise = css.process(node.attrs.style, options)
          .then(function (result) {
            node.attrs.style = result.css
          })

        promises.push(promise)
      }

      return node
    })

    return Promise.all(promises).then(function () {
      return tree
    })
  }
}
<commit_msg>fix: plugin for postcss must be function, close #10<commit_after>var postcss = require('postcss')

function indentResolve (str, options) {
  if (str.match(/\n(?!\n)\s*/g) === null) {
    return str
  }

  if (options.length === undefined) {
    options.lastLine = str.substr(str.lastIndexOf('\n') + 1)
    var newStr = str.substr(0, str.lastIndexOf('\n') + 1)
    options.length = Math.min.apply(Math, newStr.match(/\n(?!\n)\s*/g).filter(function (space) {
      return space.length > 2
    }).map(function (space) {
      return space.length
    }))

    if (options.length === Infinity) {
      return str
    }

    options.match = new Array(options.length).join(' ')
    str = str.replace(new RegExp(options.match, 'g'), '')
  } else {
    str = str.replace(/\n/g, '\n' + options.match)
    str = str.substr(0, str.lastIndexOf('\n') + 1) + options.lastLine
  }
  return str
}

module.exports = function (plugins, options, filterType) {
  plugins = [].concat(plugins).filter(plugin => typeof plugin === 'function')
  options = options || {}

  var css = postcss(plugins)

  return function posthtmlPostcss (tree) {
    var promises = []

    tree.walk(function (node) {
      var promise
      var indent = {
        length: undefined,
        match: '',
        lastLine: ''
      }

      if (node.tag === 'style' && node.content) {
        var meetsFilter = true
        if (filterType) {
          var typeAttr = (node.attrs && node.attrs.type) ? node.attrs.type.trim() : ''
          var meetsTypeAttr = filterType.test(typeAttr)
          var meetsStandardType = filterType.test('text/css') && (meetsTypeAttr || typeAttr === '')
          var meetsOtherType = !meetsStandardType && meetsTypeAttr
          meetsFilter = meetsStandardType || meetsOtherType
        }

        if (meetsFilter) {
          var styles = indentResolve([].concat(node.content).join(''), indent)
          promise = css.process(styles, options)
            .then(function (result) {
              node.content = [indentResolve(result.css, indent)]
            })

          promises.push(promise)
        }
      }

      if (node.attrs && node.attrs.style) {
        promise = css.process(node.attrs.style, options)
          .then(function (result) {
            node.attrs.style = result.css
          })

        promises.push(promise)
      }

      return node
    })

    return Promise.all(promises).then(function () {
      return tree
    })
  }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  parser: 'babel-eslint',
  settings: {
    'import/parser': 'babel-eslint',
    'import/ignore': ['node_modules', 'assets', '\\.(scss|less|css|json)$'],
  },
  rules: {
    'no-compare-neg-zero': 2,
    'no-cond-assign': 2,
    'no-console': 2,
    'no-constant-condition': 2,
    'no-control-regex': 2,
    'no-debugger': 2,
    'no-dupe-args': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty': 2,
    'no-empty-character-class': 2,
    'no-ex-assign': 2,
    'no-extra-boolean-cast': 2,
    'no-func-assign': 2,
    'no-inner-declarations': 2,
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-obj-calls': 2,
    'no-regex-spaces': 2,
    'no-sparse-arrays': 2,
    'no-template-curly-in-string': 2,
    'no-unexpected-multiline': 2,
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unsafe-negation': 2,
    'use-isnan': 2,
    'valid-typeof': 2,
    'consistent-return': 2,
    'curly': [2, 'multi-line', 'consistent'],
    'default-case': 2,
    'dot-location': [2, 'property'],
    'dot-notation': 2,
    'eqeqeq': [2, 'allow-null'],
    'guard-for-in': 2,
    'no-alert': 2,
    'no-caller': 2,
    'no-case-declarations': 2,
    'no-empty-function': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-extend-native': 2,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-global-assign': 2,
    'no-implicit-globals': 2,
    'no-implied-eval': 2,
    'no-iterator': 2,
    'no-lone-blocks': 2,
    'no-loop-func': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-new': 2,
    'no-new-func': 2,
    'no-new-wrappers': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-return-assign': 2,
    'no-script-url': 2,
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-throw-literal': 2,
    'no-unmodified-loop-condition': 2,
    'no-unused-expressions': [2, {'allowShortCircuit': true, 'allowTernary': true}],
    'no-useless-call': 2,
    'no-useless-concat': 2,
    'no-useless-escape': 2,
    'no-void': 2,
    'no-with': 2,
    'radix': 2,
    'wrap-iife': [2, 'inside'],
    'yoda': 2,
    'strict': [2, 'never'],
    'no-delete-var': 2,
    'no-shadow-restricted-names': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-unused-vars': [2, {'vars': 'all', 'args': 'none'}],
    'no-use-before-define': [2, 'nofunc'],
    'handle-callback-err': [2, '^(err|error)$'],
    'no-mixed-requires': 2,
    'no-new-require': 2,
    'no-path-concat': 2,
    'array-bracket-spacing': [2, 'never'],
    'block-spacing': [2, 'never'],
    'brace-style': [2, '1tbs', {'allowSingleLine': true}],
    'camelcase': [2, {'properties': 'never'}],
    'comma-dangle': [2, 'always-multiline'],
    'comma-spacing': [2, {'before': false, 'after': true}],
    'comma-style': [2, 'last'],
    'computed-property-spacing': [2, 'never'],
    'consistent-this': [2, 'self'],
    'eol-last': 2,
    'indent': [2, 2, {'SwitchCase': 1}],
    'jsx-quotes': [2, 'prefer-double'],
    'key-spacing': [2, {'beforeColon': false, 'afterColon': true}],
    'keyword-spacing': [2, {'before': true, 'after': true, 'overrides': {}}],
    'linebreak-style': [2, 'unix'],
    'max-len': [2, {'code': 100, 'tabWidth': 2, 'ignoreStrings': true, 'ignoreTemplateLiterals': true}],
    'new-cap': [2, {'capIsNew': false, 'newIsCap': true}],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-bitwise': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multiple-empty-lines': 2,
    'no-new-object': 2,
    'no-trailing-spaces': 2,
    'no-unneeded-ternary': [2, {'defaultAssignment': false}],
    'no-whitespace-before-property': 2,
    'object-curly-spacing': [2, 'never'],
    'quote-props': [2, 'consistent-as-needed'],
    'quotes': [2, 'single', 'avoid-escape'],
    'semi': [2, 'never'],
    'semi-spacing': [2, {'before': false, 'after': true}],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, {'anonymous': 'never', 'named': 'never', 'asyncArrow': 'always'}],
    'space-in-parens': [2, 'never'],
    'space-unary-ops': [2, {'words': true, 'nonwords': false}],
    'unicode-bom': 2,
    'arrow-parens': [2, 'always'],
    'arrow-spacing': 2,
    'constructor-super': 2,
    'no-class-assign': 2,
    'no-const-assign': 2,
    'no-dupe-class-members': 2,
    'no-duplicate-imports': 2,
    'no-new-symbol': 2,
    'no-this-before-super': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-rename': 2,
    'no-var': 2,
    'object-shorthand': 2,
    'prefer-arrow-callback': [2, {"allowUnboundThis": false}],
    'prefer-const': 2,
    'prefer-numeric-literals': 2,
    'prefer-rest-params': 2,
    'prefer-spread': 2,
    'prefer-template': 2,
    'require-await': 2,
    'require-yield': 2,
    'rest-spread-spacing': 2,
    'template-curly-spacing': 2,
    'react/jsx-boolean-value': 2,
    'react/jsx-closing-bracket-location': [2, 'line-aligned'],
    'react/jsx-curly-spacing': [2, 'never'],
    'react/jsx-equals-spacing': [2, 'never'],
    'react/jsx-indent': [2, 2],
    'react/jsx-indent-props': [2, 2],
    'react/jsx-key': 2,
    'react/jsx-no-duplicate-props': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-tag-spacing': 2,
    'react/jsx-uses-react': 2,
    'react/jsx-uses-vars': 2,
    'react/jsx-wrap-multilines': 2,
    'react/no-deprecated': 2,
    'react/no-did-mount-set-state': 2,
    'react/no-did-update-set-state': 2,
    'react/no-unknown-property': 2,
    'react/react-in-jsx-scope': 2,
    'react/self-closing-comp': 2,
    'import/export': 2,
    'import/imports-first': 2,
    'import/named': 2,
    'import/namespace': 2,
    'import/no-unresolved': [2, {'commonjs': true, 'amd': true, 'ignore': ['\\?.*$']}],
  },
  plugins: [
    'react',
    'import',
  ],
  globals: {
    'Promise': true,
    'Map': true,
    'Set': true,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    ecmaFeatures: {
      'jsx': true
    }
  }
}
<commit_msg>Make some (less important) style errors just warnings<commit_after>'use strict';

module.exports = {
  parser: 'babel-eslint',
  settings: {
    'import/parser': 'babel-eslint',
    'import/ignore': ['node_modules', 'assets', '\\.(scss|less|css|json)$'],
  },
  rules: {
    'no-compare-neg-zero': 2,
    'no-cond-assign': 2,
    'no-console': 1,
    'no-constant-condition': 2,
    'no-control-regex': 2,
    'no-debugger': 2,
    'no-dupe-args': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty': 1,
    'no-empty-character-class': 2,
    'no-ex-assign': 2,
    'no-extra-boolean-cast': 2,
    'no-func-assign': 2,
    'no-inner-declarations': 2,
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-obj-calls': 2,
    'no-regex-spaces': 2,
    'no-sparse-arrays': 2,
    'no-template-curly-in-string': 2,
    'no-unexpected-multiline': 2,
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unsafe-negation': 2,
    'use-isnan': 2,
    'valid-typeof': 2,
    'consistent-return': 2,
    'curly': [2, 'multi-line', 'consistent'],
    'default-case': 2,
    'dot-location': [2, 'property'],
    'dot-notation': 2,
    'eqeqeq': [2, 'allow-null'],
    'guard-for-in': 1,
    'no-alert': 2,
    'no-caller': 2,
    'no-case-declarations': 2,
    'no-empty-function': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-extend-native': 2,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-global-assign': 2,
    'no-implicit-globals': 2,
    'no-implied-eval': 2,
    'no-iterator': 2,
    'no-lone-blocks': 2,
    'no-loop-func': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-new': 2,
    'no-new-func': 2,
    'no-new-wrappers': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-return-assign': 2,
    'no-script-url': 2,
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-throw-literal': 2,
    'no-unmodified-loop-condition': 2,
    'no-unused-expressions': [2, {'allowShortCircuit': true, 'allowTernary': true}],
    'no-useless-call': 2,
    'no-useless-concat': 2,
    'no-useless-escape': 2,
    'no-void': 2,
    'no-with': 2,
    'radix': 2,
    'wrap-iife': [2, 'inside'],
    'yoda': 2,
    'strict': [2, 'never'],
    'no-delete-var': 2,
    'no-shadow-restricted-names': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-unused-vars': [2, {'vars': 'all', 'args': 'none'}],
    'no-use-before-define': [2, 'nofunc'],
    'handle-callback-err': [2, '^(err|error)$'],
    'no-mixed-requires': 2,
    'no-new-require': 2,
    'no-path-concat': 2,
    'array-bracket-spacing': [2, 'never'],
    'block-spacing': [2, 'never'],
    'brace-style': [2, '1tbs', {'allowSingleLine': true}],
    'camelcase': [2, {'properties': 'never'}],
    'comma-dangle': [1, 'always-multiline'],
    'comma-spacing': [1, {'before': false, 'after': true}],
    'comma-style': [2, 'last'],
    'computed-property-spacing': [2, 'never'],
    'consistent-this': [2, 'self'],
    'eol-last': 2,
    'indent': [2, 2, {'SwitchCase': 1}],
    'jsx-quotes': [2, 'prefer-double'],
    'key-spacing': [2, {'beforeColon': false, 'afterColon': true}],
    'keyword-spacing': [1, {'before': true, 'after': true, 'overrides': {}}],
    'linebreak-style': [2, 'unix'],
    'max-len': [2, {'code': 100, 'tabWidth': 2, 'ignoreStrings': true, 'ignoreTemplateLiterals': true}],
    'new-cap': [2, {'capIsNew': false, 'newIsCap': true}],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-bitwise': 1,
    'no-mixed-spaces-and-tabs': 2,
    'no-multiple-empty-lines': 1,
    'no-new-object': 2,
    'no-trailing-spaces': 2,
    'no-unneeded-ternary': [2, {'defaultAssignment': false}],
    'no-whitespace-before-property': 2,
    'object-curly-spacing': [2, 'never'],
    'quote-props': [2, 'consistent-as-needed'],
    'quotes': [2, 'single', 'avoid-escape'],
    'semi': [2, 'never'],
    'semi-spacing': [2, {'before': false, 'after': true}],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, {'anonymous': 'never', 'named': 'never', 'asyncArrow': 'always'}],
    'space-in-parens': [2, 'never'],
    'space-unary-ops': [2, {'words': true, 'nonwords': false}],
    'unicode-bom': 2,
    'arrow-parens': [2, 'always'],
    'arrow-spacing': 2,
    'constructor-super': 2,
    'no-class-assign': 2,
    'no-const-assign': 2,
    'no-dupe-class-members': 2,
    'no-duplicate-imports': 2,
    'no-new-symbol': 2,
    'no-this-before-super': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-rename': 2,
    'no-var': 2,
    'object-shorthand': 2,
    'prefer-arrow-callback': [2, {"allowUnboundThis": false}],
    'prefer-const': 1,
    'prefer-numeric-literals': 2,
    'prefer-rest-params': 2,
    'prefer-spread': 2,
    'prefer-template': 2,
    'require-await': 1,
    'require-yield': 1,
    'rest-spread-spacing': 2,
    'template-curly-spacing': 2,
    'react/jsx-boolean-value': 2,
    'react/jsx-closing-bracket-location': [2, 'line-aligned'],
    'react/jsx-curly-spacing': [2, 'never'],
    'react/jsx-equals-spacing': [2, 'never'],
    'react/jsx-indent': [2, 2],
    'react/jsx-indent-props': [2, 2],
    'react/jsx-key': 2,
    'react/jsx-no-duplicate-props': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-tag-spacing': 2,
    'react/jsx-uses-react': 2,
    'react/jsx-uses-vars': 2,
    'react/jsx-wrap-multilines': 2,
    'react/no-deprecated': 2,
    'react/no-did-mount-set-state': 2,
    'react/no-did-update-set-state': 2,
    'react/no-unknown-property': 2,
    'react/react-in-jsx-scope': 2,
    'react/self-closing-comp': 2,
    'import/export': 2,
    'import/imports-first': 2,
    'import/named': 2,
    'import/namespace': 2,
    'import/no-unresolved': [2, {'commonjs': true, 'amd': true, 'ignore': ['\\?.*$']}],
  },
  plugins: [
    'react',
    'import',
  ],
  globals: {
    'Promise': true,
    'Map': true,
    'Set': true,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    ecmaFeatures: {
      'jsx': true
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * TmpReaper provides methods to reap old files from directories
 */

var fs           = require('fs');
var path         = require('path');
var EventEmitter = require('events').EventEmitter;
var util         = require('util');
var ms           = require('./ms.js');

/**
 * Initialize a new Reaper with given options
 * @param {Object} options -> threshold : lifetime of files
 *                         -> recursive : if set to true, will reap files in subfolders
 *                         -> every     : period of time between each reap session
 *                                        if not set, reap only once
 */
var TmpReaper = function (options) {
  var self       = this;
  options        = options || {};
  self.recursive = options.recursive || false;
  self.threshold = ms(options.threshold) || 1000 * 60 * 60 * 24 * 7;
  self.cycle     = ms(options.every);
  self.dirs      = [];

  /**
   * Reap old files from a directory
   * @param  {String} dir path to dir
   */
  function reapDir(dir, callback) {
    callback = callback || function () {};

    fs.readdir(dir, function (err, files) {
      if (err) {
        self.emit('error', err);
        return;
      }

      var nbFiles = files.length;

      if (nbFiles === 0) {
        callback(true);
        return;
      }

      var processNextFile = function () {
        var f = files.pop();
        if (!f) {
          callback(nbFiles === 0);
          return;
        }
        var file = path.join(dir, f);

        fs.stat(file, function (err, stats) {
          if (err) {
            self.emit('error', err);
            processNextFile();
            return;
          }
          if (stats.isDirectory()) {
            if (self.recursive) {
              reapDir(file, function (empty) {
                if (empty) {
                  fs.rmdir(file, function (err) {
                    if (err) {
                      self.emit('error', err);
                      processNextFile();
                      return;
                    }
                    nbFiles--;
                    processNextFile();
                  });
                }
              });
            } else {
              processNextFile();
            }
          } else {
            var diff = new Date() - stats.mtime;

            if (diff > self.threshold) {
              fs.unlink(file, function (err) {
                if (err) {
                  self.emit('error', err);
                  processNextFile();
                  return;
                }
                self.emit('delete', file, stats);
                nbFiles--;
                processNextFile();
              });
            } else {
              processNextFile();
            }
          }
        });
      };

      processNextFile();
    });

    return self;
  }

  /**
   * Reap each watched dir
   */
  function reap() {
    self.dirs.forEach(function (dir) {
      fs.stat(dir, function (err, stats) {
        if (err) {
          self.emit('error', err);
          return;
        }

        if (stats.isDirectory()) {
          reapDir(dir);
        } else {
          self.emit('error', new Error('Not a directory : ' + dir));
        }
      });
    });
  }

  /**
   * Add a directory to the watching list
   * @param {String} dir
   */
  self.watch = function (dir) {
    self.dirs.push(path.normalize(dir));
    return self;
  };

  /**
   * Remove a directory from the watching list
   * @param {String} dir
   */
  self.unwatch = function (dir) {
    var i = self.dirs.indexOf(path.normalize(dir));
    if (i > -1) { self.dirs.splice(i, 1); }
    return self;
  };

  /**
   * Start reaping
   */
  self.start = function () {
    reap();
    if (self.cycle) { self.intervalID = setInterval(reap, self.cycle); }
    return self;
  };

  /**
   * Stop reaping periodically
   */
  self.stop = function () {
    if (self.intervalID) { clearInterval(self.intervalID); }
    return self;
  };
};

util.inherits(TmpReaper, EventEmitter);
module.exports = TmpReaper;
<commit_msg>use days instead of ms for defaults<commit_after>'use strict';

/**
 * TmpReaper provides methods to reap old files from directories
 */

var fs           = require('fs');
var path         = require('path');
var EventEmitter = require('events').EventEmitter;
var util         = require('util');
var ms           = require('./ms.js');

/**
 * Initialize a new Reaper with given options
 * @param {Object} options -> threshold : lifetime of files
 *                         -> recursive : if set to true, will reap files in subfolders
 *                         -> every     : period of time between each reap session
 *                                        if not set, reap only once
 */
var TmpReaper = function (options) {
  var self       = this;
  options        = options || {};
  self.recursive = options.recursive || false;
  self.threshold = ms(options.threshold || '7days');
  self.cycle     = ms(options.every);
  self.dirs      = [];

  /**
   * Reap old files from a directory
   * @param  {String} dir path to dir
   */
  function reapDir(dir, callback) {
    callback = callback || function () {};

    fs.readdir(dir, function (err, files) {
      if (err) {
        self.emit('error', err);
        return;
      }

      var nbFiles = files.length;

      if (nbFiles === 0) {
        callback(true);
        return;
      }

      var processNextFile = function () {
        var f = files.pop();
        if (!f) {
          callback(nbFiles === 0);
          return;
        }
        var file = path.join(dir, f);

        fs.stat(file, function (err, stats) {
          if (err) {
            self.emit('error', err);
            processNextFile();
            return;
          }
          if (stats.isDirectory()) {
            if (self.recursive) {
              reapDir(file, function (empty) {
                if (empty) {
                  fs.rmdir(file, function (err) {
                    if (err) {
                      self.emit('error', err);
                      processNextFile();
                      return;
                    }
                    nbFiles--;
                    processNextFile();
                  });
                }
              });
            } else {
              processNextFile();
            }
          } else {
            var diff = new Date() - stats.mtime;

            if (diff > self.threshold) {
              fs.unlink(file, function (err) {
                if (err) {
                  self.emit('error', err);
                  processNextFile();
                  return;
                }
                self.emit('delete', file, stats);
                nbFiles--;
                processNextFile();
              });
            } else {
              processNextFile();
            }
          }
        });
      };

      processNextFile();
    });

    return self;
  }

  /**
   * Reap each watched dir
   */
  function reap() {
    self.dirs.forEach(function (dir) {
      fs.stat(dir, function (err, stats) {
        if (err) {
          self.emit('error', err);
          return;
        }

        if (stats.isDirectory()) {
          reapDir(dir);
        } else {
          self.emit('error', new Error('Not a directory : ' + dir));
        }
      });
    });
  }

  /**
   * Add a directory to the watching list
   * @param {String} dir
   */
  self.watch = function (dir) {
    self.dirs.push(path.normalize(dir));
    return self;
  };

  /**
   * Remove a directory from the watching list
   * @param {String} dir
   */
  self.unwatch = function (dir) {
    var i = self.dirs.indexOf(path.normalize(dir));
    if (i > -1) { self.dirs.splice(i, 1); }
    return self;
  };

  /**
   * Start reaping
   */
  self.start = function () {
    reap();
    if (self.cycle) { self.intervalID = setInterval(reap, self.cycle); }
    return self;
  };

  /**
   * Stop reaping periodically
   */
  self.stop = function () {
    if (self.intervalID) { clearInterval(self.intervalID); }
    return self;
  };
};

util.inherits(TmpReaper, EventEmitter);
module.exports = TmpReaper;
<|endoftext|>


<commit_before>var creds = require(process.env.HOME + '/.googlePlayCreds.json');
var PlayMusic = require('playmusic');
var mkdirp = require('mkdirp');
var https = require('https');
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
var stream = require('stream');

var config, player;

var gmusicBackend = {};
gmusicBackend.name = 'gmusic';

// TODO: seeking
var encodeSong = function(origStream, seek, songID, progCallback, errCallback) {
    var incompletePath = config.songCachePath + '/gmusic/incomplete/' + songID + '.opus';
    var incompleteStream = fs.createWriteStream(incompletePath, {flags: 'w'});
    var encodedPath = config.songCachePath + '/gmusic/' + songID + '.opus';

    var command = ffmpeg(origStream)
        .noVideo()
        //.inputFormat('mp3')
        //.inputOption('-ac 2')
        .audioCodec('libopus')
        .audioBitrate('192')
        .format('opus')
        .on('error', function(err) {
            console.log('gmusic: error while transcoding ' + songID + ': ' + err);
            if(fs.existsSync(incompletePath))
                fs.unlinkSync(incompletePath);
            errCallback(err);
        })

    var opusStream = command.pipe(null, {end: true});
    opusStream.on('data', function(chunk) {
        incompleteStream.write(chunk, undefined, function() {
            progCallback(chunk.length, false);
        });
    });
    opusStream.on('end', function() {
        incompleteStream.end(undefined, undefined, function() {
            console.log('transcoding ended for ' + songID);

            // TODO: we don't know if transcoding ended successfully or not,
            // and there might be a race condition between errCallback deleting
            // the file and us trying to move it to the songCache

            // atomically move result to encodedPath
            if(fs.existsSync(incompletePath))
                fs.renameSync(incompletePath, encodedPath);

            progCallback(0, true);
        });
    });

    console.log('transcoding ' + songID + '...');
    return function(err) {
        command.kill();
        console.log('gmusic: canceled preparing: ' + songID + ': ' + err);
        if(fs.existsSync(incompletePath))
            fs.unlinkSync(incompletePath);
        errCallback('canceled preparing: ' + songID + ': ' + err);
    };
};

var gmusicDownload = function(songID, progCallback, errCallback) {
    var req = null;
    var gmusicStream = new stream.PassThrough();

    var doDownload = function(streamUrl) {
        if(streamUrl) {
            console.log('gmusic: downloading song ' + songID);

            req = https.request(streamUrl, function(res) {
                res.pipe(gmusicStream, {end: false});

                res.on('end', function() {
                    if(res.statusCode === 302) { // redirect
                        console.log('gmusic: redirected. retrying with new URL');
                        res.unpipe();
                        doDownload(res.headers.location);
                    } else if (res.statusCode === 200) {
                        console.log('gmusic: download finished');
                        gmusicStream.end();
                    } else {
                        gmusicStream.end();
                        console.log('ERROR in gmusic: unknown status code ' + res.statusCode);
                        if(errCallback)
                            errCallback('unknown status code ' + res.statusCode);
                    }
                });
            });
            req.on('error', function(e) {
                console.log('ERROR in gmusic: ' + e + ' while fetching! reconnecting in 5s...');
                setTimeout(function() {
                    gmusicBackend.init(function() {
                        console.log('ERROR in gmusic while fetching! now reconnected to gmusic');
                        gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                            doDownload(streamUrl);
                        }, function(err) {
                            errCallback(err);
                        });
                    });
                }, 5000);
            });
            req.end();
        } else {
            gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                doDownload(streamUrl);
            }, function(err) {
                errCallback(err);
            });
        }
    };

    /*
    return function(err) {
        // TODO: this doesn't seem to work very well...
        if(command)
            command.kill();
        if(req)
            req.abort();

        if(fs.existsSync(filePath))
            fs.unlinkSync(filePath);
        if(fs.existsSync(incompleteFilePath))
            fs.unlinkSync(incompleteFilePath);

        console.log('gmusic: canceled preparing: ' + songID + ': ' + err);

        errCallback();
    };
    */

    doDownload();

    var cancelEncoding = encodeSong(gmusicStream, 0, songID, progCallback, errCallback);
    return function(err) {
        if(req)
            req.abort();
        cancelEncoding(err);
    };
};

// cache songID to disk.
// on success: progCallback must be called with true as argument
// on failure: errCallback must be called with error message
// returns a function that cancels preparing
gmusicBackend.prepareSong = function(songID, progCallback, errCallback) {
    var filePath = config.songCachePath + '/gmusic/' + songID + '.opus';

    if(fs.existsSync(filePath)) {
        progCallback(0, true);
    } else {
        return gmusicDownload(songID, progCallback, errCallback);
    }
};

// search for music from the backend
// on success: callback must be called with a list of song objects
// on failure: errCallback must be called with error message
gmusicBackend.search = function(query, callback, errCallback) {
    gmusicBackend.pm.search(query.terms, config.searchResultCnt, function(data) {
        var songs;
        var results = {};
        results.songs = {};

        if(data.entries) {
            songs = data.entries.filter(function(entry) {
                return entry.type === '1'; // songs only, no albums/artists
            });

            for(var i = 0; i < songs.length; i++) {
                results.songs[songs[i].track.nid] = {
                    artist: songs[i].track.artist,
                    title: songs[i].track.title,
                    album: songs[i].track.album,
                    albumArt: null, // TODO: can we add this?
                    duration: songs[i].track.durationMillis,
                    songID: songs[i].track.nid,
                    score: songs[i].score,
                    backendName: 'gmusic',
                    format: 'opus'
                };
            }
        }

        callback(results);
    }, function(err) {
        errCallback('error while searching gmusic: ' + err);
    });
};

// called when partyplay is started to initialize the backend
// do any necessary initialization here
gmusicBackend.init = function(_player, callback) {
    player = _player;
    config = _player.config;

    mkdirp(config.songCachePath + '/gmusic/incomplete');

    // initialize google play music backend
    gmusicBackend.pm = new PlayMusic();
    gmusicBackend.pm.init(creds, callback);
};

module.exports = gmusicBackend;
<commit_msg>don't callback with done if we fail<commit_after>var creds = require(process.env.HOME + '/.googlePlayCreds.json');
var PlayMusic = require('playmusic');
var mkdirp = require('mkdirp');
var https = require('https');
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
var stream = require('stream');

var config, player;

var gmusicBackend = {};
gmusicBackend.name = 'gmusic';

// TODO: seeking
var encodeSong = function(origStream, seek, songID, progCallback, errCallback) {
    var incompletePath = config.songCachePath + '/gmusic/incomplete/' + songID + '.opus';
    var incompleteStream = fs.createWriteStream(incompletePath, {flags: 'w'});
    var encodedPath = config.songCachePath + '/gmusic/' + songID + '.opus';

    var command = ffmpeg(origStream)
        .noVideo()
        //.inputFormat('mp3')
        //.inputOption('-ac 2')
        .audioCodec('libopus')
        .audioBitrate('192')
        .format('opus')
        .on('error', function(err) {
            console.log('gmusic: error while transcoding ' + songID + ': ' + err);
            if(fs.existsSync(incompletePath))
                fs.unlinkSync(incompletePath);
            errCallback(err);
        })

    var opusStream = command.pipe(null, {end: true});
    opusStream.on('data', function(chunk) {
        incompleteStream.write(chunk, undefined, function() {
            progCallback(chunk.length, false);
        });
    });
    opusStream.on('end', function() {
        incompleteStream.end(undefined, undefined, function() {
            console.log('transcoding ended for ' + songID);

            // TODO: we don't know if transcoding ended successfully or not,
            // and there might be a race condition between errCallback deleting
            // the file and us trying to move it to the songCache

            // atomically move result to encodedPath
            if(fs.existsSync(incompletePath)) {
                fs.renameSync(incompletePath, encodedPath);
                progCallback(0, true);
            } else {
                progCallback(0, false);
            }
        });
    });

    console.log('transcoding ' + songID + '...');
    return function(err) {
        command.kill();
        console.log('gmusic: canceled preparing: ' + songID + ': ' + err);
        if(fs.existsSync(incompletePath))
            fs.unlinkSync(incompletePath);
        errCallback('canceled preparing: ' + songID + ': ' + err);
    };
};

var gmusicDownload = function(songID, progCallback, errCallback) {
    var req = null;
    var gmusicStream = new stream.PassThrough();

    var doDownload = function(streamUrl) {
        if(streamUrl) {
            console.log('gmusic: downloading song ' + songID);

            req = https.request(streamUrl, function(res) {
                res.pipe(gmusicStream, {end: false});

                res.on('end', function() {
                    if(res.statusCode === 302) { // redirect
                        console.log('gmusic: redirected. retrying with new URL');
                        res.unpipe();
                        doDownload(res.headers.location);
                    } else if (res.statusCode === 200) {
                        console.log('gmusic: download finished');
                        gmusicStream.end();
                    } else {
                        gmusicStream.end();
                        console.log('ERROR in gmusic: unknown status code ' + res.statusCode);
                        if(errCallback)
                            errCallback('unknown status code ' + res.statusCode);
                    }
                });
            });
            req.on('error', function(e) {
                console.log('ERROR in gmusic: ' + e + ' while fetching! reconnecting in 5s...');
                setTimeout(function() {
                    gmusicBackend.init(function() {
                        console.log('ERROR in gmusic while fetching! now reconnected to gmusic');
                        gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                            doDownload(streamUrl);
                        }, function(err) {
                            errCallback(err);
                        });
                    });
                }, 5000);
            });
            req.end();
        } else {
            gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                doDownload(streamUrl);
            }, function(err) {
                errCallback(err);
            });
        }
    };

    /*
    return function(err) {
        // TODO: this doesn't seem to work very well...
        if(command)
            command.kill();
        if(req)
            req.abort();

        if(fs.existsSync(filePath))
            fs.unlinkSync(filePath);
        if(fs.existsSync(incompleteFilePath))
            fs.unlinkSync(incompleteFilePath);

        console.log('gmusic: canceled preparing: ' + songID + ': ' + err);

        errCallback();
    };
    */

    doDownload();

    var cancelEncoding = encodeSong(gmusicStream, 0, songID, progCallback, errCallback);
    return function(err) {
        if(req)
            req.abort();
        cancelEncoding(err);
    };
};

// cache songID to disk.
// on success: progCallback must be called with true as argument
// on failure: errCallback must be called with error message
// returns a function that cancels preparing
gmusicBackend.prepareSong = function(songID, progCallback, errCallback) {
    var filePath = config.songCachePath + '/gmusic/' + songID + '.opus';

    if(fs.existsSync(filePath)) {
        progCallback(0, true);
    } else {
        return gmusicDownload(songID, progCallback, errCallback);
    }
};

// search for music from the backend
// on success: callback must be called with a list of song objects
// on failure: errCallback must be called with error message
gmusicBackend.search = function(query, callback, errCallback) {
    gmusicBackend.pm.search(query.terms, config.searchResultCnt, function(data) {
        var songs;
        var results = {};
        results.songs = {};

        if(data.entries) {
            songs = data.entries.filter(function(entry) {
                return entry.type === '1'; // songs only, no albums/artists
            });

            for(var i = 0; i < songs.length; i++) {
                results.songs[songs[i].track.nid] = {
                    artist: songs[i].track.artist,
                    title: songs[i].track.title,
                    album: songs[i].track.album,
                    albumArt: null, // TODO: can we add this?
                    duration: songs[i].track.durationMillis,
                    songID: songs[i].track.nid,
                    score: songs[i].score,
                    backendName: 'gmusic',
                    format: 'opus'
                };
            }
        }

        callback(results);
    }, function(err) {
        errCallback('error while searching gmusic: ' + err);
    });
};

// called when partyplay is started to initialize the backend
// do any necessary initialization here
gmusicBackend.init = function(_player, callback) {
    player = _player;
    config = _player.config;

    mkdirp(config.songCachePath + '/gmusic/incomplete');

    // initialize google play music backend
    gmusicBackend.pm = new PlayMusic();
    gmusicBackend.pm.init(creds, callback);
};

module.exports = gmusicBackend;
<|endoftext|>


<commit_before>var _ = require('lodash')

module.exports = function(schema, options) {
  options = _.extend(
    { emitChangedOnCreated: false,
      emitChangedOnVirtualFields: false
    },
    options || {}
  )

  if (_.isString(options.emitChangedOnVirtualFields)) {
    options.emitChangedOnVirtualFields = [].concat(options.emitChangedOnVirtualFields)
  }

  if (options.emitChangedOnVirtualFields) {
    schema.pre('set', function(next, fieldPath, fieldValue) {
      this.$__.virtualFieldsPreviousValue = this.$__.virtualFieldsPreviousValue || {}
      for (var index in options.emitChangedOnVirtualFields) {
        var virtualFieldPath = options.emitChangedOnVirtualFields[index]
        this.$__.virtualFieldsPreviousValue[virtualFieldPath] = this.get(virtualFieldPath)
      }
      next()
      return this
    })
  }

  schema.pre('save', function(next) {
    // the current model is already an event emitter
    var fieldsThatHaveChanged = this.modifiedPaths(),
        model = this.model(this.constructor.modelName),
        alreadyEmitted = {},
        index, virtualFieldPath

    var shouldEmitChangedWhenCreated = (!this.isNew || (this.isNew && options.emitChangedOnCreated))

    if (fieldsThatHaveChanged && shouldEmitChangedWhenCreated) {
      model.emit('changed', this)

      for (index in fieldsThatHaveChanged) {
        var eventKey = 'changed:' + fieldsThatHaveChanged[index]
        if (!alreadyEmitted[eventKey]) {
          model.emit(eventKey, this)
          alreadyEmitted[eventKey] = true
        }
      }

      checkIfVirtualFieldsAreChanged(this, model)
    }

    if (this.isNew && options.emitChangedOnVirtualFields) {
      this.$__.virtualFieldsPreviousValue = this.$__.virtualFieldsPreviousValue || {}

      for (index in options.emitChangedOnVirtualFields) {
        virtualFieldPath = options.emitChangedOnVirtualFields[index]
        this.$__.virtualFieldsPreviousValue[virtualFieldPath] = this.get(virtualFieldPath)
      }
    }

    if (this.isNew) {
      model.emit('created', this)
    }

    next()
  })

  schema.post('remove', function() {
    this.model(this.constructor.modelName).emit('removed', this)
  });

  function checkIfVirtualFieldsAreChanged(doc, model) {
    if (options.emitChangedOnVirtualFields) {
      doc.$__.virtualFieldsPreviousValue = doc.$__.virtualFieldsPreviousValue || {}

      for (var index in options.emitChangedOnVirtualFields) {
        var virtualFieldPath = options.emitChangedOnVirtualFields[index]
        var previousVirtualFieldValue = doc.$__.virtualFieldsPreviousValue[virtualFieldPath]

        if (!_.isEqual(previousVirtualFieldValue, doc.get(virtualFieldPath))) {
          model.emit('changed:' + virtualFieldPath, doc)
        }
      }
    }
  }
}
<commit_msg>Extract rememberInitialValueOfVirtualFields<commit_after>var _ = require('lodash')

module.exports = function(schema, options) {
  options = _.extend(
    { emitChangedOnCreated: false,
      emitChangedOnVirtualFields: false
    },
    options || {}
  )

  if (_.isString(options.emitChangedOnVirtualFields)) {
    options.emitChangedOnVirtualFields = [].concat(options.emitChangedOnVirtualFields)
  }

  if (options.emitChangedOnVirtualFields) {
    schema.pre('set', function(next, fieldPath, fieldValue) {
      this.$__.virtualFieldsPreviousValue = this.$__.virtualFieldsPreviousValue || {}
      for (var index in options.emitChangedOnVirtualFields) {
        var virtualFieldPath = options.emitChangedOnVirtualFields[index]
        this.$__.virtualFieldsPreviousValue[virtualFieldPath] = this.get(virtualFieldPath)
      }
      next()
      return this
    })
  }

  schema.pre('save', function(next) {
    // the current model is already an event emitter
    var fieldsThatHaveChanged = this.modifiedPaths(),
        model = this.model(this.constructor.modelName),
        alreadyEmitted = {},
        index, virtualFieldPath

    var shouldEmitChangedWhenCreated = (!this.isNew || (this.isNew && options.emitChangedOnCreated))

    if (fieldsThatHaveChanged && shouldEmitChangedWhenCreated) {
      model.emit('changed', this)

      for (index in fieldsThatHaveChanged) {
        var eventKey = 'changed:' + fieldsThatHaveChanged[index]
        if (!alreadyEmitted[eventKey]) {
          model.emit(eventKey, this)
          alreadyEmitted[eventKey] = true
        }
      }

      checkIfVirtualFieldsAreChanged(this, model)
    }

    rememberInitialValueOfVirtualFields(this)

    if (this.isNew) {
      model.emit('created', this)
    }

    next()
  })

  schema.post('remove', function() {
    this.model(this.constructor.modelName).emit('removed', this)
  });

  function checkIfVirtualFieldsAreChanged(doc, model) {
    if (options.emitChangedOnVirtualFields) {
      doc.$__.virtualFieldsPreviousValue = doc.$__.virtualFieldsPreviousValue || {}

      for (var index in options.emitChangedOnVirtualFields) {
        var virtualFieldPath = options.emitChangedOnVirtualFields[index]
        var previousVirtualFieldValue = doc.$__.virtualFieldsPreviousValue[virtualFieldPath]

        if (!_.isEqual(previousVirtualFieldValue, doc.get(virtualFieldPath))) {
          model.emit('changed:' + virtualFieldPath, doc)
        }
      }
    }
  }

  function rememberInitialValueOfVirtualFields(doc) {
    if (doc.isNew && options.emitChangedOnVirtualFields) {
      doc.$__.virtualFieldsPreviousValue = doc.$__.virtualFieldsPreviousValue || {}

      for (var index in options.emitChangedOnVirtualFields) {
        var virtualFieldPath = options.emitChangedOnVirtualFields[index]
        doc.$__.virtualFieldsPreviousValue[virtualFieldPath] = doc.get(virtualFieldPath)
      }
    }
  }
}
<|endoftext|>


<commit_before>var path = require('path'),
    program = require('commander'),
    pjson = require('./package.json');

function collectPaths(value, paths) {
  paths.push(value);
  return paths;
}

program
  .version(pjson.version)
  .description(pjson.description)
  .option('-s, --steps <path>', 'path to step definitions. defaults to ./step-definitions', './step-definitions')
  .option('-p, --pageObjects <path>', 'path to page objects. defaults to ./page-objects', './page-objects')
  .option('-o, --sharedObjects [paths]', 'path to shared objects (repeatable). defaults to ./shared-objects', collectPaths, ['./shared-objects'])
  .option('-b, --browser <path>', 'name of browser to use. defaults to chrome', /^(chrome|firefox|phantomjs)$/i, 'chrome')
  .option('-t, --tags <tagName>', 'name of tag to run')
  .parse(process.argv);

program.on('--help', function() {
    console.log('  For more details please visit https://github.com/john-doherty/selenium-cucumber-js#readme\n');
});

// store browserName globally (used within world.js to build driver)
global.browserName = program.browser;

// used within world.js to import page objects
global.pageObjectPath = path.resolve(program.pageObjects);

// used within world.js to import shared objects into the shared namespace
global.sharedObjectPaths = program.sharedObjects.map(function(item){
    return path.resolve(item);
});

// rewrite command line switches for cucumber
process.argv.splice(2, 100);

// add cucumber world as first required script (this sets up the globals)
process.argv.push('-r');
process.argv.push(path.join(__dirname, 'runtime/world.js'));

// add path to import step definitions
process.argv.push('-r');
process.argv.push(program.steps);

// add tag
if (program.tags) {
    process.argv.push('-t');
    process.argv.push(program.tags);
}

if (process.platform === 'win32') {
    require('./node_modules/.bin/cucumber-js');
}
else {
    require('./node_modules/.bin/cucumber.js');
}<commit_msg>modified runner to resolve windows issues<commit_after>'use strict';

var path = require('path'),
    program = require('commander'),
    pjson = require('./package.json'),
    Cucumber = require('cucumber');

function collectPaths(value, paths) {
  paths.push(value);
  return paths;
}

program
  .version(pjson.version)
  .description(pjson.description)
  .option('-s, --steps <path>', 'path to step definitions. defaults to ./step-definitions', './step-definitions')
  .option('-p, --pageObjects <path>', 'path to page objects. defaults to ./page-objects', './page-objects')
  .option('-o, --sharedObjects [paths]', 'path to shared objects (repeatable). defaults to ./shared-objects', collectPaths, ['./shared-objects'])
  .option('-b, --browser <path>', 'name of browser to use. defaults to chrome', /^(chrome|firefox|phantomjs)$/i, 'chrome')
  .option('-t, --tags <tagName>', 'name of tag to run')
  .parse(process.argv);

program.on('--help', function() {
    console.log('  For more details please visit https://github.com/john-doherty/selenium-cucumber-js#readme\n');
});

// store browserName globally (used within world.js to build driver)
global.browserName = program.browser;

// used within world.js to import page objects
global.pageObjectPath = path.resolve(program.pageObjects);

// used within world.js to import shared objects into the shared namespace
global.sharedObjectPaths = program.sharedObjects.map(function(item){
    return path.resolve(item);
});

// rewrite command line switches for cucumber
process.argv.splice(2, 100);

// add cucumber world as first required script (this sets up the globals)
process.argv.push('-r');
process.argv.push(path.join(__dirname, 'runtime/world.js'));

// add path to import step definitions
process.argv.push('-r');
process.argv.push(program.steps);

// add tag
if (program.tags) {
    process.argv.push('-t');
    process.argv.push(program.tags);
}

//
// execute cucumber
//
var cucumberCli = Cucumber.Cli(process.argv);

cucumberCli.run(function (succeeded) {
  var code = succeeded ? 0 : 1;

  function exitNow() {
    process.exit(code);
  }

  if (process.stdout.write('')) {
    exitNow();
  } else {
    // write() returned false, kernel buffer is not empty yet...
    process.stdout.on('drain', exitNow);
  }
});
<|endoftext|>


<commit_before>/**
 * Exterme Go Horse TypeScript import refactoring lib
 *
 * TODO LIST
 * - Generate import path from classes that was not imported in file
 * - Convert this to TypeScript code and remove the 'Extreme Go Horse' code
 * - Write unit tests to each situation of file importation or refactoring
 * - Convert it into a VSCode plugin
 * - Publish this in npm
 * - Write code to cover importation from node_modules
 */
const fs = require('fs');
const args = process.argv.splice(2);
const config = { dir: process.cwd() };
args.forEach(arg => {
	if (/^--dir=.*$/.test(arg)) {
		let subdir = arg.replace(/^.*=/, '');
		if (subdir === '.') {
			subdir = '';
		}
		config.dir = `${process.cwd()}\\${subdir}`;
	}
});

const exportMapByClass = {};
const exportMapByPath = {};

function Directory(path) {
	this.path = path;
	this.content = [];

	this.forEach = function(calle){
		this.content.forEach((f) => calle(f));
	}

	// constructor
	fs.readdirSync(this.path).forEach(path => {
		const fullpath = `${this.path}\\${path}`;
		if (fs.lstatSync(fullpath).isDirectory()) {
			this.content.push(new Directory(fullpath));
		} else {
			if (/\.ts$/.test(fullpath)) {
				this.content.push(new TSFile(fullpath));
			}
		}
	});
}

function TSFile(path) {
	this.path = path.replace(/\/|(\\\\)|\\/g, '/');
	this.content = String(fs.readFileSync(this.path));
	this.exports = [];
	this.imports = {};

	exportMapByPath[this.path] = this;

	(this.content.match(/import.*["'].*["'];?/g) || []).forEach(_import => {
		_import = new TSFileImport(this.path, _import);
		this.imports[_import.from] = _import;
	});

	(this.content.match(/export\s+(class|enum)\s+[^\s]*\s*{/g) || []).forEach(match => {
		var thisClass = match.replace(/^export\s+(class|enum)\s+|\s*{$/g, '');
		this.exports.push(thisClass);
		var arr = exportMapByClass[thisClass] || [];
		arr.push(this);
		exportMapByClass[thisClass] = arr;
	});
}

function TSFileImport(path, _import) {
	var parentPath = path.replace(/[\/\\][^\/\\]*$/, '');
	this._import = _import;
	this.classes = _import.replace(/^import {|}.*$|\s/g, '').trim().split(',');
	this.from = fundirPaths(parentPath, _import.replace(/^[^'"]*|['"]\.?|;/g, ''))+'.ts';
	this.fromNodeModules = !_import.match(/['"](\.|app\/)/);
}

function pathGenerator(importer, importing) {
	importer = importer.replace(/\/|(\\\\)|\\/g, '/').replace(/\/[^\/]*\.ts$/, '').split('/');
	importing = importing.replace(/\/|(\\\\)|\\/g, '/').split('/');
	var length = importer.length;
	var importFinal = '';

	for (let i = 0; i < importer.length; i++) {
		if (importer[i] !== importing[i]) {
			length = i;
			break;
		}
	}

	var relativeImport = importer.splice(length);
	importing = importing.splice(length);
	if (relativeImport.length == 0) {
		importFinal = './';
	} else {
		relativeImport.forEach(() => importFinal += '../');
	}

	return importFinal + importing.join('/').replace(/\.ts$/, '');
}

function fundirPaths(basePath, restoPath) {
    basePath = basePath.replace(/\/|(\\\\)|\\/g, '/').split('/');
    restoPath = restoPath.replace(/\/|(\\\\)|\\/g, '/').split('/');
    let quantosRetorna = 0;

    restoPath.forEach((resto) => {
        if (resto) {
            if (resto == '..') {
                basePath.pop();
            } else {
                basePath.push(resto);
            }
        }
    });

    return basePath.join('/');
}

(function reviewCode(project){
	project.forEach(item => {
		if (item instanceof Directory) {
			reviewCode(item);
		} else {
			Object.keys(item.imports).forEach(importation => {
				if (!item.imports[importation].fromNodeModules) {
					if (!exportMapByPath[importation]) {
						var pathAntigoDaClasse = null, naoUsar = false;
                        item.imports[importation].classes.forEach((cls) => {
							if (!pathAntigoDaClasse) {
								pathAntigoDaClasse = exportMapByClass[cls];
							} else if (pathAntigoDaClasse != exportMapByClass[cls]) {
								console.error(`O sistema não importará automaticamente a classe ${cls} pois ela possui mais de um path`);
								naoUsar = true;
							}
						});

                        if (!pathAntigoDaClasse) {
                            naoUsar = true;}

						if (pathAntigoDaClasse && pathAntigoDaClasse.length > 1) {
							naoUsar = true;
							console.error('Uma classe tem mais de um path, o refatorador não irá reimportá-la');
						}

						if (!naoUsar) {
                            console.log(`Problema em "${item.imports[importation]._import}", corrigindo com "${pathAntigoDaClasse[0].path}"`);
							var newImport = item.imports[importation]._import.replace(/from.*$/, `from '${pathGenerator(item.path, pathAntigoDaClasse[0].path)}';`);
							item.content = item.content.replace(item.imports[importation]._import, newImport);
							fs.writeFileSync(item.path, item.content);
						}
					}
				}
			});
		}
	});
})(new Directory(config.dir));
<commit_msg>Corrigindo bug onde a importação de interfaces e types eram ignoradas<commit_after>/**
 * Exterme Go Horse TypeScript import refactoring lib
 *
 * TODO LIST
 * - Generate import path from classes that was not imported in file
 * - Convert this to TypeScript code and remove the 'Extreme Go Horse' code
 * - Write unit tests to each situation of file importation or refactoring
 * - Convert it into a VSCode plugin
 * - Publish this in npm
 * - Write code to cover importation from node_modules
 */
const fs = require('fs');
const args = process.argv.splice(2);
const config = { dir: process.cwd() };
args.forEach(arg => {
	if (/^--dir=.*$/.test(arg)) {
		let subdir = arg.replace(/^.*=/, '');
		if (subdir === '.') {
			subdir = '';
		}
		config.dir = `${process.cwd()}\\${subdir}`;
	}
});

const exportMapByClass = {};
const exportMapByPath = {};

function Directory(path) {
	this.path = path;
	this.content = [];

	this.forEach = function(calle){
		this.content.forEach((f) => calle(f));
	}

	// constructor
	fs.readdirSync(this.path).forEach(path => {
		const fullpath = `${this.path}\\${path}`;
		if (fs.lstatSync(fullpath).isDirectory()) {
			this.content.push(new Directory(fullpath));
		} else {
			if (/\.ts$/.test(fullpath)) {
				this.content.push(new TSFile(fullpath));
			}
		}
	});
}

function TSFile(path) {
	this.path = path.replace(/\/|(\\\\)|\\/g, '/');
	this.content = String(fs.readFileSync(this.path));
	this.exports = [];
	this.imports = {};

	exportMapByPath[this.path] = this;

	(this.content.match(/import.*["'].*["'];?/g) || []).forEach(_import => {
		_import = new TSFileImport(this.path, _import);
		this.imports[_import.from] = _import;
	});

	(this.content.match(/export\s+(class|enum|type|interface)\s+[^\s]*\s*{/g) || []).forEach(match => {
		var thisClass = match.replace(/^export\s+(class|enum|type|interface)\s+|\s*{$/g, '');
		this.exports.push(thisClass);
		var arr = exportMapByClass[thisClass] || [];
		arr.push(this);
		exportMapByClass[thisClass] = arr;
	});
}

function TSFileImport(path, _import) {
	var parentPath = path.replace(/[\/\\][^\/\\]*$/, '');
	this._import = _import;
	this.classes = _import.replace(/^import {|}.*$|\s/g, '').trim().split(',');
	this.from = fundirPaths(parentPath, _import.replace(/^[^'"]*|['"]\.?|;/g, ''))+'.ts';
	this.fromNodeModules = !_import.match(/['"](\.|app\/)/);
}

function pathGenerator(importer, importing) {
	importer = importer.replace(/\/|(\\\\)|\\/g, '/').replace(/\/[^\/]*\.ts$/, '').split('/');
	importing = importing.replace(/\/|(\\\\)|\\/g, '/').split('/');
	var length = importer.length;
	var importFinal = '';

	for (let i = 0; i < importer.length; i++) {
		if (importer[i] !== importing[i]) {
			length = i;
			break;
		}
	}

	var relativeImport = importer.splice(length);
	importing = importing.splice(length);
	if (relativeImport.length == 0) {
		importFinal = './';
	} else {
		relativeImport.forEach(() => importFinal += '../');
	}

	return importFinal + importing.join('/').replace(/\.ts$/, '');
}

function fundirPaths(basePath, restoPath) {
    basePath = basePath.replace(/\/|(\\\\)|\\/g, '/').split('/');
    restoPath = restoPath.replace(/\/|(\\\\)|\\/g, '/').split('/');
    let quantosRetorna = 0;

    restoPath.forEach((resto) => {
        if (resto) {
            if (resto == '..') {
                basePath.pop();
            } else {
                basePath.push(resto);
            }
        }
    });

    return basePath.join('/');
}

(function reviewCode(project){
	project.forEach(item => {
		if (item instanceof Directory) {
			reviewCode(item);
		} else {
			Object.keys(item.imports).forEach(importation => {
				if (!item.imports[importation].fromNodeModules) {
					if (!exportMapByPath[importation]) {
						var pathAntigoDaClasse = null, naoUsar = false;
                        item.imports[importation].classes.forEach((cls) => {
							if (!pathAntigoDaClasse) {
								pathAntigoDaClasse = exportMapByClass[cls];
							} else if (pathAntigoDaClasse != exportMapByClass[cls]) {
								console.error(`O sistema não importará automaticamente a classe ${cls} pois ela possui mais de um path`);
								naoUsar = true;
							}
						});

                        if (!pathAntigoDaClasse) {
                            naoUsar = true;}

						if (pathAntigoDaClasse && pathAntigoDaClasse.length > 1) {
							naoUsar = true;
							console.error('Uma classe tem mais de um path, o refatorador não irá reimportá-la');
						}

						if (!naoUsar) {
                            console.log(`Problema em "${item.imports[importation]._import}", corrigindo com "${pathAntigoDaClasse[0].path}"`);
							var newImport = item.imports[importation]._import.replace(/from.*$/, `from '${pathGenerator(item.path, pathAntigoDaClasse[0].path)}';`);
							item.content = item.content.replace(item.imports[importation]._import, newImport);
							fs.writeFileSync(item.path, item.content);
						}
					}
				}
			});
		}
	});
})(new Directory(config.dir));<|endoftext|>


<commit_before>const combine = (combiner, ...observables) => next => {
  let state = Array(observables.length)

  observables.forEach((base, index) => base(value => {
    state[index] = value

    if(Object.keys(state).length === state.length)
      next(combiner(state))
  }))
}

const constant = (base, value) => map(base, () => value)

const filter = (base, predicate) => next =>
  base(value => predicate(value) && next(value))

const flatMap = (base, mapper) => next =>
  base(value => mapper(value)(next))

const from = values => next => values.forEach(next)

const just = value => next => next(value)

const map = (base, mapper) => next =>
  base(value => next(mapper(value)))

const merge = (...observables) => next => observables.forEach(base => base(next))

const periodic = interval => next => setInterval(next, interval)

const scan = (base, reducer, initial) => next => {
  let state = initial
  base(value => {
    state = reducer(state, value)
    next(state)
  })
}

export {
  combine,
  constant,
  filter,
  from,
  just,
  map,
  merge,
  periodic,
  scan,
}
<commit_msg>reident merge code<commit_after>const combine = (combiner, ...observables) => next => {
  let state = Array(observables.length)

  observables.forEach((base, index) => base(value => {
    state[index] = value

    if(Object.keys(state).length === state.length)
      next(combiner(state))
  }))
}

const constant = (base, value) => map(base, () => value)

const filter = (base, predicate) => next =>
  base(value => predicate(value) && next(value))

const flatMap = (base, mapper) => next =>
  base(value => mapper(value)(next))

const from = values => next => values.forEach(next)

const just = value => next => next(value)

const map = (base, mapper) => next =>
  base(value => next(mapper(value)))

const merge = (...observables) => next =>
  observables.forEach(base => base(next))

const periodic = interval => next => setInterval(next, interval)

const scan = (base, reducer, initial) => next => {
  let state = initial
  base(value => {
    state = reducer(state, value)
    next(state)
  })
}

export {
  combine,
  constant,
  filter,
  from,
  just,
  map,
  merge,
  periodic,
  scan,
}
<|endoftext|>


<commit_before>process.on("uncaughtException", function (err) {
    console.error (err);
});

// dependencies
var Statique = global.Statique = require ("statique")
  , Utils = require ("./utils")
  , Config = global.Config = require ("./config.js")
  , Url = require("url")
  , Http = require ("http")
  , Apis = require ("./apis")
  , ipaddress = Config.ipaddress
  , port      = Config.port
  ;

// sessions
global.sessions = {};

// attach core pages
Config.gitSite.parsed.roots.pages["/login"] = {
    url: "/core/html/login.html"
  , visible: false
};

Config.gitSite.parsed.roots.pages["/blog"] = {
    url: "/core/html/blog.html"
  , label: "Blog"
  , order: 19
};

Config.gitSite.parsed.roots.pages["/admin"] = {
    url: "/core/html/admin.html"
  , label: "Admin"
  , order: 21
  , loggedIn: true
};

// statique config
Statique
    .server({root: Config.gitSite.paths.ROOT})
    .setRoutes(Config.gitSite.parsed.roots.pages)
  ;

// create server
Http.createServer (function(req, res) {

    var pathName = Url.parse(req.url, true).pathname;

    // get route
    var route = Statique.getRoute (pathName)
      , isBlogPost =  (new RegExp (SITE_CONFIG.blog.url + "\/([a-z]|[0-9])")).test (pathName)
      ;

    if (route || isBlogPost) {
        Apis["handlePage:" + req.method](req, res, pathName, route, null, isBlogPost);
        return;
    }

    // serve files
    Statique.serve (req, res);
}).listen(port, ipaddress);

// print some output
console.log("Server running at http://localhost:%d", port);
<commit_msg>Output the ipaddress and port<commit_after>process.on("uncaughtException", function (err) {
    console.error (err);
});

// dependencies
var Statique = global.Statique = require ("statique")
  , Utils = require ("./utils")
  , Config = global.Config = require ("./config.js")
  , Url = require("url")
  , Http = require ("http")
  , Apis = require ("./apis")
  , ipaddress = Config.ipaddress
  , port      = Config.port
  ;

// sessions
global.sessions = {};

// attach core pages
Config.gitSite.parsed.roots.pages["/login"] = {
    url: "/core/html/login.html"
  , visible: false
};

Config.gitSite.parsed.roots.pages["/blog"] = {
    url: "/core/html/blog.html"
  , label: "Blog"
  , order: 19
};

Config.gitSite.parsed.roots.pages["/admin"] = {
    url: "/core/html/admin.html"
  , label: "Admin"
  , order: 21
  , loggedIn: true
};

// statique config
Statique
    .server({root: Config.gitSite.paths.ROOT})
    .setRoutes(Config.gitSite.parsed.roots.pages)
  ;

// create server
Http.createServer (function(req, res) {

    var pathName = Url.parse(req.url, true).pathname;

    // get route
    var route = Statique.getRoute (pathName)
      , isBlogPost =  (new RegExp (SITE_CONFIG.blog.url + "\/([a-z]|[0-9])")).test (pathName)
      ;

    if (route || isBlogPost) {
        Apis["handlePage:" + req.method](req, res, pathName, route, null, isBlogPost);
        return;
    }

    // serve files
    Statique.serve (req, res);
}).listen(port, ipaddress);

// print some output
console.log("Server running at http://%d:%d", ipaddress, port);
<|endoftext|>


<commit_before>'use strict';
var gutil = require('gulp-util');
var File = gutil.File;
var through = require('through2');
var SVGO = require('svgo');

/**
 * File must be a buffer and not a string.
 * @param {string} file Name of the output compressed JSON file.
 * @param {Object} opts Other options (not yet used).
 */
module.exports = function(file, opts) {
  if (!file) {
    throw new PluginError(
        'gulp-svg-json-spritesheet',
        'Missing parameter file for gulp-svg-json-spritesheet');
  }

  opts = opts || {};
  var spritesheet = {};
  var svgCompressor = new SVGO();

  function compressEachFile(file, encoding, callback) {
    if (file.isNull()) {
      callback(null, file);
      return;
    }

    if (file.isStream()) {
      callback(new gutil.PluginError(
          'gulp-svg-spritesheet', 'Streaming not supported'));
      return;
    }

    try {
      svgCompressor.optimize(file.contents.toString(), function(result) {
        var filePath = file.path;

        // Gets file name from file path.
        var fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
        spritesheet[fileName] = result;
      });
    } catch (error) {
      this.emit('error', new gutil.PluginError('gulp-svg-spritesheet', error));
    }

    callback();
  }

  /**
   * Once all files have been compressed, return compressed JSON file.
   * @param {Function} callback
   */
  function convertToJson(callback) {
    var output = new File(file);
    output.contents = new Buffer(JSON.stringify(spritesheet, null, '\t'));
    output.path = file;
    this.push(output);
    callback();
  }

  return through.obj(compressEachFile, convertToJson);
};
<commit_msg>Wrong module name again in index.js<commit_after>'use strict';
var gutil = require('gulp-util');
var File = gutil.File;
var through = require('through2');
var SVGO = require('svgo');

/**
 * File must be a buffer and not a string.
 * @param {string} file Name of the output compressed JSON file.
 * @param {Object} opts Other options (not yet used).
 */
module.exports = function(file, opts) {
  if (!file) {
    throw new PluginError(
        'gulp-svg-json-spritesheet',
        'Missing parameter file for gulp-svg-json-spritesheet');
  }

  opts = opts || {};
  var spritesheet = {};
  var svgCompressor = new SVGO();

  function compressEachFile(file, encoding, callback) {
    if (file.isNull()) {
      callback(null, file);
      return;
    }

    if (file.isStream()) {
      callback(new gutil.PluginError(
          'gulp-svg-json-spritesheet', 'Streaming not supported'));
      return;
    }

    try {
      svgCompressor.optimize(file.contents.toString(), function(result) {
        var filePath = file.path;

        // Gets file name from file path.
        var fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
        spritesheet[fileName] = result;
      });
    } catch (error) {
      this.emit('error', new gutil.PluginError('gulp-svg-json-spritesheet', error));
    }

    callback();
  }

  /**
   * Once all files have been compressed, return compressed JSON file.
   * @param {Function} callback
   */
  function convertToJson(callback) {
    var output = new File(file);
    output.contents = new Buffer(JSON.stringify(spritesheet, null, '\t'));
    output.path = file;
    this.push(output);
    callback();
  }

  return through.obj(compressEachFile, convertToJson);
};
<|endoftext|>