<commit_before>// TODO: dhtPort and torrentPort should be consistent between restarts
// TODO: peerId and nodeId should be consistent between restarts

module.exports = WebTorrent

var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('extend.js')
var FileReadStream = require('filestream/read')
var FSStorage = require('./lib/fs-storage') // browser exclude
var hat = require('hat')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var speedometer = require('speedometer')
var Storage = require('./lib/storage')
var stream = require('stream')
var Torrent = require('./lib/torrent')

inherits(WebTorrent, EventEmitter)

/**
 * WebTorrent Client
 * @param {Object} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  if (!opts) opts = {}
  EventEmitter.call(self)

  self.torrentPort = opts.torrentPort || 0
  self.tracker = (opts.tracker !== undefined) ? opts.tracker : true
  self.torrents = []

  self.downloadSpeed = speedometer()
  self.uploadSpeed = speedometer()

  self.storage = typeof opts.storage === 'function'
    ? opts.storage
    : (opts.storage !== false && typeof FSStorage === 'function' /* browser exclude */)
      ? FSStorage
      : Storage

  self.peerId = opts.peerId === undefined
    ? new Buffer('-WW0001-' + hat(48), 'utf8')
    : typeof opts.peerId === 'string'
      ? new Buffer(opts.peerId, 'utf8')
      : opts.peerId
  self.peerIdHex = self.peerId.toString('hex')

  self.nodeId = opts.nodeId === undefined
    ? new Buffer(hat(160), 'hex')
    : typeof opts.nodeId === 'string'
      ? new Buffer(opts.nodeId, 'hex')
      : opts.nodeId
  self.nodeIdHex = self.nodeId.toString('hex')

  // TODO: implement webtorrent-dht
  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))
    self.dht.listen(opts.dhtPort)
  }

  debug('new webtorrent (peerId %s, nodeId %s)', self.peerIdHex, self.nodeIdHex)

  if (typeof loadIPSet === 'function') {
    loadIPSet(opts.blocklist, function (err, ipSet) {
      self.blocked = ipSet
      ready()
    })
  } else process.nextTick(ready)

  function ready () {
    self.ready = true
    self.emit('ready')
  }
}

/**
 * Seed ratio for all torrents in the client.
 * @type {number}
 */
Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var self = this
    var uploaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var downloaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0) || 1
    return uploaded / downloaded
  }
})

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array.
 *
 * @param  {string|Buffer|Object} torrentId
 * @return {Torrent}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  var parsed = parseTorrent(torrentId)
  if (!parsed || !parsed.infoHash) return null
  for (var i = 0, len = self.torrents.length; i < len; i++) {
    var torrent = self.torrents[i]
    if (torrent.infoHash === parsed.infoHash) return torrent
  }
  return null
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 *
 * `torrentId` can be one of:
 *   - magnet uri (utf8 string)
 *   - torrent file (buffer)
 *   - info hash (hex string or buffer)
 *   - parsed torrent (from [parse-torrent](https://github.com/feross/parse-torrent))
 *   - http/https url to a torrent file (string)
 *   - filesystem path to a torrent file (string)
 *
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add =
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  var self = this
  debug('add %s', torrentId)
  if (typeof opts === 'function') {
    ontorrent = opts
    opts = {}
  }
  if (!opts) opts = {}

  opts.client = self
  opts.storage = opts.storage || self.storage

  if (opts.tmp) opts.storageOpts = { tmp: opts.tmp }

  var torrent = new Torrent(torrentId, extend({ client: self }, opts))
  self.torrents.push(torrent)

  function clientOnTorrent (_torrent) {
    if (torrent.infoHash === _torrent.infoHash) {
      ontorrent(torrent)
      self.removeListener('torrent', clientOnTorrent)
    }
  }
  if (ontorrent) self.on('torrent', clientOnTorrent)

  torrent.on('error', function (err) {
    self.emit('error', err, torrent)
  })

  torrent.on('listening', function (port) {
    self.emit('listening', port, torrent)
  })

  torrent.on('ready', function () {
    // Emit 'torrent' when a torrent is ready to be used
    debug('torrent')
    self.emit('torrent', torrent)
  })

  return torrent
}

/**
 * Start seeding a new torrent.
 *
 * `input` can be any of the following:
 *   - path to the file or folder on filesystem (string)
 *   - W3C File object (from an `<input>` or drag and drop)
 *   - W3C FileList object (basically an array of `File` objects)
 *   - Array of `File` objects
 *
 * @param  {string|File|FileList|Blob|Buffer|Array.<File|Blob|Buffer>} input
 * @param  {Object} opts
 * @param  {function} onseed
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (typeof opts === 'function') {
    onseed = opts
    opts = {}
  }

  // TODO: support `input` as string, or array of strings

  if (typeof FileList !== 'undefined' && input instanceof FileList)
    input = Array.prototype.slice.call(input)

  if (isBlob(input) || Buffer.isBuffer(input)) {
    input = [ input ]
  }

  var streams = input.map(function (item) {
    if (isBlob(item)) return new FileReadStream(item)
    else if (Buffer.isBuffer(item)) {
      var s = new stream.PassThrough()
      s.end(item)
      return s
    } else throw new Error('unsupported input type to `seed`')
  })

  var torrent
  createTorrent(input, opts, function (err, torrentBuf) {
    if (err) return self.emit('error', err)
    self.add(torrentBuf, opts, function (_torrent) {
      torrent = _torrent
      torrent.storage.load(
        streams,
        function (err) {
          if (err) return self.emit('error', err)
          self.emit('seed', torrent)
        })
    })
  })

  function clientOnSeed (_torrent) {
    if (torrent.infoHash === _torrent.infoHash) {
      onseed(torrent)
      self.removeListener('seed', clientOnSeed)
    }
  }
  if (onseed) self.on('seed', clientOnSeed)
}

/**
 * Remove a torrent from the client.
 *
 * @param  {string|Buffer}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  var self = this
  var torrent = self.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  debug('remove')
  self.torrents.splice(self.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

/**
 * Destroy the client, including all torrents and connections to peers.
 *
 * @override
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  var self = this
  debug('destroy')

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) {
      self.remove(torrent.infoHash, cb)
    }
  })

  if (self.dht) tasks.push(function (cb) {
    self.dht.destroy(cb)
  })

  parallel(tasks, cb)
}

/**
 * Check if `obj` is a W3C Blob object (which is the superclass of W3C File)
 * @param  {*} obj
 * @return {boolean}
 */
function isBlob (obj) {
  return typeof Blob !== 'undefined' && obj instanceof Blob
}
<commit_msg>suppress event emitter warning in production<commit_after>// TODO: dhtPort and torrentPort should be consistent between restarts
// TODO: peerId and nodeId should be consistent between restarts

module.exports = WebTorrent

var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('extend.js')
var FileReadStream = require('filestream/read')
var FSStorage = require('./lib/fs-storage') // browser exclude
var hat = require('hat')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var speedometer = require('speedometer')
var Storage = require('./lib/storage')
var stream = require('stream')
var Torrent = require('./lib/torrent')

inherits(WebTorrent, EventEmitter)

/**
 * WebTorrent Client
 * @param {Object} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  if (!opts) opts = {}
  EventEmitter.call(self)
  if (!debug.enabled) self.setMaxListeners(0)

  self.torrentPort = opts.torrentPort || 0
  self.tracker = (opts.tracker !== undefined) ? opts.tracker : true
  self.torrents = []

  self.downloadSpeed = speedometer()
  self.uploadSpeed = speedometer()

  self.storage = typeof opts.storage === 'function'
    ? opts.storage
    : (opts.storage !== false && typeof FSStorage === 'function' /* browser exclude */)
      ? FSStorage
      : Storage

  self.peerId = opts.peerId === undefined
    ? new Buffer('-WW0001-' + hat(48), 'utf8')
    : typeof opts.peerId === 'string'
      ? new Buffer(opts.peerId, 'utf8')
      : opts.peerId
  self.peerIdHex = self.peerId.toString('hex')

  self.nodeId = opts.nodeId === undefined
    ? new Buffer(hat(160), 'hex')
    : typeof opts.nodeId === 'string'
      ? new Buffer(opts.nodeId, 'hex')
      : opts.nodeId
  self.nodeIdHex = self.nodeId.toString('hex')

  // TODO: implement webtorrent-dht
  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))
    self.dht.listen(opts.dhtPort)
  }

  debug('new webtorrent (peerId %s, nodeId %s)', self.peerIdHex, self.nodeIdHex)

  if (typeof loadIPSet === 'function') {
    loadIPSet(opts.blocklist, function (err, ipSet) {
      self.blocked = ipSet
      ready()
    })
  } else process.nextTick(ready)

  function ready () {
    self.ready = true
    self.emit('ready')
  }
}

/**
 * Seed ratio for all torrents in the client.
 * @type {number}
 */
Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var self = this
    var uploaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var downloaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0) || 1
    return uploaded / downloaded
  }
})

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array.
 *
 * @param  {string|Buffer|Object} torrentId
 * @return {Torrent}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  var parsed = parseTorrent(torrentId)
  if (!parsed || !parsed.infoHash) return null
  for (var i = 0, len = self.torrents.length; i < len; i++) {
    var torrent = self.torrents[i]
    if (torrent.infoHash === parsed.infoHash) return torrent
  }
  return null
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 *
 * `torrentId` can be one of:
 *   - magnet uri (utf8 string)
 *   - torrent file (buffer)
 *   - info hash (hex string or buffer)
 *   - parsed torrent (from [parse-torrent](https://github.com/feross/parse-torrent))
 *   - http/https url to a torrent file (string)
 *   - filesystem path to a torrent file (string)
 *
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add =
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  var self = this
  debug('add %s', torrentId)
  if (typeof opts === 'function') {
    ontorrent = opts
    opts = {}
  }
  if (!opts) opts = {}

  opts.client = self
  opts.storage = opts.storage || self.storage

  if (opts.tmp) opts.storageOpts = { tmp: opts.tmp }

  var torrent = new Torrent(torrentId, extend({ client: self }, opts))
  self.torrents.push(torrent)

  function clientOnTorrent (_torrent) {
    if (torrent.infoHash === _torrent.infoHash) {
      ontorrent(torrent)
      self.removeListener('torrent', clientOnTorrent)
    }
  }
  if (ontorrent) self.on('torrent', clientOnTorrent)

  torrent.on('error', function (err) {
    self.emit('error', err, torrent)
  })

  torrent.on('listening', function (port) {
    self.emit('listening', port, torrent)
  })

  torrent.on('ready', function () {
    // Emit 'torrent' when a torrent is ready to be used
    debug('torrent')
    self.emit('torrent', torrent)
  })

  return torrent
}

/**
 * Start seeding a new torrent.
 *
 * `input` can be any of the following:
 *   - path to the file or folder on filesystem (string)
 *   - W3C File object (from an `<input>` or drag and drop)
 *   - W3C FileList object (basically an array of `File` objects)
 *   - Array of `File` objects
 *
 * @param  {string|File|FileList|Blob|Buffer|Array.<File|Blob|Buffer>} input
 * @param  {Object} opts
 * @param  {function} onseed
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (typeof opts === 'function') {
    onseed = opts
    opts = {}
  }

  // TODO: support `input` as string, or array of strings

  if (typeof FileList !== 'undefined' && input instanceof FileList)
    input = Array.prototype.slice.call(input)

  if (isBlob(input) || Buffer.isBuffer(input)) {
    input = [ input ]
  }

  var streams = input.map(function (item) {
    if (isBlob(item)) return new FileReadStream(item)
    else if (Buffer.isBuffer(item)) {
      var s = new stream.PassThrough()
      s.end(item)
      return s
    } else throw new Error('unsupported input type to `seed`')
  })

  var torrent
  createTorrent(input, opts, function (err, torrentBuf) {
    if (err) return self.emit('error', err)
    self.add(torrentBuf, opts, function (_torrent) {
      torrent = _torrent
      torrent.storage.load(
        streams,
        function (err) {
          if (err) return self.emit('error', err)
          self.emit('seed', torrent)
        })
    })
  })

  function clientOnSeed (_torrent) {
    if (torrent.infoHash === _torrent.infoHash) {
      onseed(torrent)
      self.removeListener('seed', clientOnSeed)
    }
  }
  if (onseed) self.on('seed', clientOnSeed)
}

/**
 * Remove a torrent from the client.
 *
 * @param  {string|Buffer}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  var self = this
  var torrent = self.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  debug('remove')
  self.torrents.splice(self.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

/**
 * Destroy the client, including all torrents and connections to peers.
 *
 * @override
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  var self = this
  debug('destroy')

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) {
      self.remove(torrent.infoHash, cb)
    }
  })

  if (self.dht) tasks.push(function (cb) {
    self.dht.destroy(cb)
  })

  parallel(tasks, cb)
}

/**
 * Check if `obj` is a W3C Blob object (which is the superclass of W3C File)
 * @param  {*} obj
 * @return {boolean}
 */
function isBlob (obj) {
  return typeof Blob !== 'undefined' && obj instanceof Blob
}
<|endoftext|>


<commit_before>var fs = require('fs'),
	path = require('path');
	useragent = require('useragent'),
	uglify    = require('uglify-js'),
	AliasResolver = require('./aliases'),
	lookupAgent = require('./agent');

// Load additional useragent features: primarily to use: agent.satisfies to
// test a browser version against a semver string
require('useragent/features');

"use strict";

var polyfillSourceFolder = path.join(__dirname, 'source');

var sources = {},
	aliases = {};

fs.readdirSync(polyfillSourceFolder).forEach(function (polyfillName) {

	var config = require(path.join(polyfillSourceFolder, polyfillName, 'config.json')),
		polyfillSourcePath = path.join(polyfillSourceFolder, polyfillName, 'polyfill.js');

	// Read each file and store in a map for quick lookup
	sources[polyfillName] = {
		file: fs.readFileSync(polyfillSourcePath, 'utf8'),
		config: config
	};

	// Store alias names in a map for efficient lookup, mapping aliases to
	// polyfillNames.  An alias can map to many polyfill names. So a group
	// of polyfills can be aliased under the same name.  This is why an
	// array is created and used for the value in the map.
	config.aliases = config.aliases || [];
	config.aliases.forEach(function(aliasName) {
		if (aliases[aliasName]) {
			aliases[aliasName].push(polyfillName);
		} else {
			aliases[aliasName] = [ polyfillName ];
		}
	});
});

function getPolyfillString(options) {
	var ua = useragent.lookup(options.uaString),
		uaFamily = lookupAgent(ua.family.toLowerCase()),
		explainerComment = [
			options.url,
			'Detected ' + uaFamily + '/' + ua.toVersion()
		],
		currentPolyfills = {};

	// Browsers don't really use semantic versioning but tend to at least
	// have a major and minor version.  This normalises the patch version so that
	// semantic version comparison is consistent.
	if (!isNumeric(ua.patch)) {
		ua.patch = '0';
	}

	var includePolyfills = options.polyfills.forEach(function(polyfill) {
		var polyfillSource = sources[polyfill.name];

		if (!polyfillSource) {
			explainerComment.push('"' + polyfill.name + '" does not match any polyfills');
			return;
		}

		if (polyfill.flags.indexOf('maybe') !== -1) {
			var polyfillConfig = polyfillSource.config,
				browsersConfigured = polyfillConfig && polyfillConfig.browsers;

			if (!(browsersConfigured)) {
				return;
			}

			var browserVersion = polyfillConfig.browsers[uaFamily];
			if (!(browserVersion && ua.satisfies(browserVersion))) {
				return;
			}
		}

		explainerComment.push(polyfill.name + ' - ' + polyfill.aliasOf + ' (LICENSE TODO)');
		currentPolyfills[polyfill.name] = polyfillSource.file;

	});

	var builtExplainerComment = '/* ' + explainerComment.join('\n * ') + '\n */\n';
	var builtPolyfillString = objectJoin(currentPolyfills, '\n');

	if (options.minified) {
		builtPolyfillString = uglify.minify(builtPolyfillString, {fromString: true}).code;
	}

	return builtExplainerComment + builtPolyfillString;
}

module.exports = {
	getPolyfills: getPolyfillString,
	aliases: aliases
};

/**
 * jQuery: isNumeric
 * https://github.com/jquery/jquery/blob/bbdfbb4ee859fe9319b348d88120ddc2c9efbd63/src/core.js#L212
 */
function isNumeric(obj) {
	return !Array.isArray(obj) && (((obj- parseFloat(obj)) + 1) >= 0);
}

/**
 * For each key in an object/hash map, join the value associated with the
 * key.  This allows you to perform a join operation over what is effectively
 * a set.
 *
 * Example:
 *
 * objectJoin({ x: 'A', y: 'B' }, ',');
 *
 * Result:
 * 'A,B'
 *
 * In Harmony (ES6) Just use 'Set':
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
 */
function objectJoin(obj, joinString) {
	return Object.keys(obj).map(function(key) { return obj[key]; }).join(joinString);
}
<commit_msg>Add back the alias expansion logic<commit_after>var fs = require('fs'),
	path = require('path');
	useragent = require('useragent'),
	uglify    = require('uglify-js'),
	AliasResolver = require('./aliases'),
	lookupAgent = require('./agent');

// Load additional useragent features: primarily to use: agent.satisfies to
// test a browser version against a semver string
require('useragent/features');

"use strict";

var polyfillSourceFolder = path.join(__dirname, 'source');

var sources = {},
	aliases = {};

fs.readdirSync(polyfillSourceFolder).forEach(function (polyfillName) {

	var config = require(path.join(polyfillSourceFolder, polyfillName, 'config.json')),
		polyfillSourcePath = path.join(polyfillSourceFolder, polyfillName, 'polyfill.js');

	// Read each file and store in a map for quick lookup
	sources[polyfillName] = {
		file: fs.readFileSync(polyfillSourcePath, 'utf8'),
		config: config
	};

	// Store alias names in a map for efficient lookup, mapping aliases to
	// polyfillNames.  An alias can map to many polyfill names. So a group
	// of polyfills can be aliased under the same name.  This is why an
	// array is created and used for the value in the map.
	config.aliases = config.aliases || [];
	config.aliases.forEach(function(aliasName) {
		if (aliases[aliasName]) {
			aliases[aliasName].push(polyfillName);
		} else {
			aliases[aliasName] = [ polyfillName ];
		}
	});
});

var aliasResolver = AliasResolver.createDefault(aliases);

function getPolyfillString(options) {
	var ua = useragent.lookup(options.uaString),
		uaFamily = lookupAgent(ua.family.toLowerCase()),
		explainerComment = [
			options.url,
			'Detected ' + uaFamily + '/' + ua.toVersion()
		],
		currentPolyfills = {};

	// Browsers don't really use semantic versioning but tend to at least
	// have a major and minor version.  This normalises the patch version so that
	// semantic version comparison is consistent.
	if (!isNumeric(ua.patch)) {
		ua.patch = '0';
	}

	var expandedPolyfillList = aliasResolver.resolve(options.polyfills),
		includePolyfills = expandedPolyfillList.forEach(function(polyfill) {
		var polyfillSource = sources[polyfill.name];

		if (!polyfillSource) {
			explainerComment.push('"' + polyfill.name + '" does not match any polyfills');
			return;
		}

		if (polyfill.flags.indexOf('maybe') !== -1) {
			var polyfillConfig = polyfillSource.config,
				browsersConfigured = polyfillConfig && polyfillConfig.browsers;

			if (!(browsersConfigured)) {
				return;
			}

			var browserVersion = polyfillConfig.browsers[uaFamily];
			if (!(browserVersion && ua.satisfies(browserVersion))) {
				return;
			}
		}

		explainerComment.push(polyfill.name + ' - ' + polyfill.aliasOf + ' (LICENSE TODO)');
		currentPolyfills[polyfill.name] = polyfillSource.file;

	});

	var builtExplainerComment = '/* ' + explainerComment.join('\n * ') + '\n */\n';
	var builtPolyfillString = objectJoin(currentPolyfills, '\n');

	if (options.minified) {
		builtPolyfillString = uglify.minify(builtPolyfillString, {fromString: true}).code;
	}

	return builtExplainerComment + builtPolyfillString;
}

module.exports = {
	getPolyfills: getPolyfillString,
	aliases: aliases
};

/**
 * jQuery: isNumeric
 * https://github.com/jquery/jquery/blob/bbdfbb4ee859fe9319b348d88120ddc2c9efbd63/src/core.js#L212
 */
function isNumeric(obj) {
	return !Array.isArray(obj) && (((obj- parseFloat(obj)) + 1) >= 0);
}

/**
 * For each key in an object/hash map, join the value associated with the
 * key.  This allows you to perform a join operation over what is effectively
 * a set.
 *
 * Example:
 *
 * objectJoin({ x: 'A', y: 'B' }, ',');
 *
 * Result:
 * 'A,B'
 *
 * In Harmony (ES6) Just use 'Set':
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
 */
function objectJoin(obj, joinString) {
	return Object.keys(obj).map(function(key) { return obj[key]; }).join(joinString);
}
<|endoftext|>


<commit_before>/**
 * mukla <https://github.com/tunnckoCore/mukla>
 *
 * Copyright (c) 2014-2015 Charlike Mike Reagent, contributors.
 * Released under the MIT license.
 */

'use strict';

var assert = require('assert')
var chalk = require('chalk');
var logSymbols = require('log-symbols');

var mukla = module.exports = function mukla(title, fn) {
  var promise = Promise.resolve(1);
  if (!fn && typeof fn !== 'function') {
    return mode(title, promise);
  }
  return promise.then(fn.bind(this)).then(function() {
    console.log(logSymbols.success, title);
  })
  .catch(function(err) {
    console.log(logSymbols.error, title);
  });
}

function mode(subtitle, promise) {
  var o = {};
  Object.keys(assert).forEach(function(method) {
    o[method] = function(actual, expected) {
      return promise.then(function(res) {
        assert[method].apply(assert[method], [actual, expected, subtitle]);
        console.log('  %s %s', logSymbols.success, subtitle);
        return res;
      })
      .catch(function(err) {
        console.log('  %s %s', logSymbols.error, subtitle);
      });
    }
  });
  return o;
}

// var mukla = module.exports = function mukla(title, fn) {
//   var promise = Promise.resolve('')
//   if (title && fn) {
//     promise
//     .then(fn.bind(this))
//     .then(function() {
//       console.log(logSymbols.success, title);
//     })
//     .catch(function(err) {
//       console.log(logSymbols.error, title);
//     })
//   }

//   if (!fn) {
//     return asserter(title, promise);
//   }
//   return promise;
// };

// function asserter(title, promise) {
//   var o = {};
//   Object.keys(assert).forEach(function(key) {
//     o[key] = function(actual, expected) {
//       return promise
//       .then(function() {
//         assert[key].apply(assert[key], [actual, expected, title]);
//       })
//       .catch(function(err) {
//         console.log('%s %s',logSymbols.error, title);
//       })
//     }
//   })
//   return o;
// }

<commit_msg>cleanup, npm@0.0.0<commit_after>/**
 * mukla <https://github.com/tunnckoCore/mukla>
 *
 * Copyright (c) 2014-2015 Charlike Mike Reagent, contributors.
 * Released under the MIT license.
 */

'use strict';

var assert = require('assert')
var chalk = require('chalk');
var logSymbols = require('log-symbols');

var mukla = module.exports = function mukla(title, fn) {
  var promise = Promise.resolve(1);
  if (!fn && typeof fn !== 'function') {
    return mode(title, promise);
  }
  return promise.then(fn.bind(this)).then(function() {
    console.log(logSymbols.success, title);
  })
  .catch(function(err) {
    console.log(logSymbols.error, title);
  });
}

function mode(subtitle, promise) {
  var o = {};
  Object.keys(assert).forEach(function(method) {
    o[method] = function(actual, expected) {
      return promise.then(function(res) {
        assert[method].apply(assert[method], [actual, expected, subtitle]);
        console.log('  %s %s', logSymbols.success, subtitle);
        return res;
      })
      .catch(function(err) {
        console.log('  %s %s', logSymbols.error, subtitle);
      });
    }
  });
  return o;
}
<|endoftext|>


<commit_before>'use strict'
<commit_msg>Add index file with requires for each module<commit_after>'use strict'

module.exports = {
  Number: require('./number'),
  Expiration: require('./expiration'),
  Cvc: require('./cvc')
}
<|endoftext|>


<commit_before>/*
 * @package dav-server
 * @copyright Copyright(c) 2016 PDXHub <eric AT PDXHub.org>
 * @author Eric Drechsel <eric AT PDXHub.org>
 * @license http://github.com/edrex/dav-server/blob/master/LICENSE MIT License
 */
"use strict";

var args = require('minimist')(process.argv.slice(2), {boolean: ['debug','h']});
var fs = require('fs')
var path = require('path')
var jsDAV = require("jsdav");
var jsDAV_Locks_Backend_FS = require("jsdav/lib/DAV/plugins/locks/fs");

var usage = "dav-server [--debug] [-i LISTEN_IP] [-p PORT] rootDir";

function exitMsg(msg, code){
  console.log(msg);
  process.exit(code || 0);
}

if (args.h || args._.length !== 1) exitMsg(usage);
if (args.debug) jsDAV.debugMode = true;

var rootDir = path.join(args._.shift(), '/');
if (!fs.existsSync(rootDir)) exitMsg(rootDir+" does not exist.");
if (!fs.statSync(rootDir).isDirectory()) exitMsg(rootDir+": not a directory.");
jsDAV.createServer({
  node: rootDir,
  locksBackend: jsDAV_Locks_Backend_FS.new(rootDir)
}, 8000);
<commit_msg>add -i and -p<commit_after>/*
 * @package dav-server
 * @copyright Copyright(c) 2016 PDXHub <eric AT PDXHub.org>
 * @author Eric Drechsel <eric AT PDXHub.org>
 * @license http://github.com/edrex/dav-server/blob/master/LICENSE MIT License
 */
"use strict";

var args = require('minimist')(process.argv.slice(2), {boolean: ['debug','h'], string: ['i', 'p']});
var fs = require('fs')
var path = require('path')
var jsDAV = require("jsdav");
var jsDAV_Locks_Backend_FS = require("jsdav/lib/DAV/plugins/locks/fs");

var usage = "dav-server [--debug] [-i LISTEN_IP] [-p PORT] rootDir";

function exitMsg(msg, code){
  console.log(msg);
  process.exit(code || 0);
}

if (args.h || args._.length !== 1) exitMsg(usage);
if (args.debug) jsDAV.debugMode = true;

var rootDir = path.join(args._.shift(), '/');
if (!fs.existsSync(rootDir)) exitMsg(rootDir+" does not exist.");
if (!fs.statSync(rootDir).isDirectory()) exitMsg(rootDir+": not a directory.");
jsDAV.createServer({
  node: rootDir,
  locksBackend: jsDAV_Locks_Backend_FS.new(rootDir)
}, args.p || 8000, args.i || '127.0.0.1');
<|endoftext|>


<commit_before>const DEBUG = true
const DATA_PROVIDERS_URL = "https://raw.githubusercontent.com/neoranga55/bitcoin-price-ticker/refactor-to-use-firefox-frames/data/data-providers.json"
const ADDON_UPDATE_DOCUMENT_URL = "http://neoranga55.github.io/bitcoin-price-ticker/"

// The main module of the Add-on.
var ui = require('sdk/ui')
const {Cc, Ci, Cu} = require("chrome")
Cu.import("resource://gre/modules/AddonManager.jsm") // Addon Manager required to know addon version
const setTimeout = require("sdk/timers").setTimeout
const ADDON_ID = "jid0-ziK34XHkBWB9ezxd4l9Q1yC7RP0@jetpack"
const DEFAULT_REFRESH_RATE = 60
const DEFAULT_FONT_SIZE = 14

var Preferences = require('sdk/simple-prefs');
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.ADDON_ID.");
var Request = require("sdk/request").Request;
var tabs = require("sdk/tabs");

var orderedTickers = new Array()
// Ids of all tickers available. Ticker configuration for each ID is in file data/js/providers.js
var tickers = {} // Store all tickers here

exports.main = function() {

  function getPreference(prefName, type) {
    if (typeof Preferences.prefs[prefName] == "undefined") {
      if (DEBUG) console.log("bitcoin-price-ticker addon error: " + prefName + " preference is not defined")
      switch (type) {
        case "boolean":
          return false
        case "integer":
          return -1
        case "string":
          return ""
        default:
          return null
      }
    }
    return Preferences.prefs[prefName]
  }

  function getBooleanPreference(prefName) {
    return getPreference(prefName, "boolean")
  }

  function getIntegerPreference(prefName) {
    return getPreference(prefName, "integer")
  }

  function getStringPreference(prefName) {
    return getPreference(prefName, "string")
  }

  function getBackgroundColor(id) {
    var lowId = id.toLowerCase()
    var otherBgCryptos = [ 'dogecoin', 'worldcoin', 'namecoin', 'auroracoin', 'blackcoin', 'nxt',
      'bitshares', 'ripple', 'maidsafe', 'bitcoindark', 'monero', 'dash', 'burst' ]
    for (var i in otherBgCryptos) {
      if (lowId.indexOf(otherBgCryptos[i]) != -1) {  // Alt-coin
        if (getBooleanPreference("other-background")) {
          return otherBgCryptos[i]
        }
      }
    }
    if (lowId.indexOf("litecoin") != -1) {
      if (getBooleanPreference("silver-background")) { // Currency is Litecoin
        return "silver"
      }
    } else if (getBooleanPreference("gold-background")) { // Currency is Bitcoin
      return "gold"
    }
    return null
  }

  function getTickerConfigurationData(tickerId) {
    var fontSize = getIntegerPreference("defaultFontSize");
    if (fontSize <= 0) {
      fontSize = DEFAULT_FONT_SIZE
    }
    var refreshRate = getIntegerPreference("Timer");
    if (refreshRate < 1) {
      refreshRate = DEFAULT_REFRESH_RATE;
    }
    var tickerData = {
      id: tickerId,
      enabled: getBooleanPreference("p" + tickerId),
      currencyPosition: getStringPreference("show-currency-label"),
      color: getStringPreference("p" + tickerId + "Color"),
      fontSize: fontSize,
      background: getBackgroundColor(tickerId),
      updateInterval: refreshRate
    }
    return tickerData
  }

  function loadTicker(tickerId) {
    var tickerData = getTickerConfigurationData(tickerId)
    tickers[tickerId] = tickerData
    updateTickerConfiguration(tickerData)
    return tickerData
  }

  // Use tickers enabled in preferences to load in that order regardless of stored order
  function loadDefaultTickers() {
    for (var tickerId in tickers) {
      if ( getBooleanPreference('p' + tickerId) ) { // Create Ticker
        var tickerData = loadTicker(tickerId)
        if (tickerData.enabled) orderedTickers.push(tickerId)
      }
    }
    if ((orderedTickers != null) && (orderedTickers.length > 0)) {
      storeTickersOrder()
    }
  }

  // Load the order of the tickers and simultaneously create them
  function loadTickersInOrder() {
    var orderedActiveTickers = ""
    try {
      orderedActiveTickers = prefs.getCharPref("extensions.ADDON_ID.tickers_order")
      if (orderedActiveTickers.length < 1) { // There is no order of tickers in addon set yet
        loadDefaultTickers()
        return
      }
      var listOrderedTickers = orderedActiveTickers.split(',')
      if (listOrderedTickers.length < 1) { // There is no order of tickers in addon set yet
        loadDefaultTickers()
        return
      }
      for (var i in listOrderedTickers) {
        var tickerData = loadTicker(listOrderedTickers[i])
        if (tickerData.enabled) orderedTickers.push(tickerId)
      }
    } catch (e) { // There is no order of tickers in addon set yet
      loadDefaultTickers()
    }
  }

  // Store the order of the active tickers
  function storeTickersOrder() {
    if ((orderedTickers == null) || (orderedTickers.length == 0)) {
      loadDefaultTickers()
    } else {
      var orderedActiveTickers = ""
      if ((orderedTickers != null) && (orderedTickers.length > 0)) {
        for (var i in orderedTickers) { // Traverse skipping empty
          if (orderedActiveTickers.length > 0) {
            orderedActiveTickers += "," + orderedTickers[i]
          } else {
            orderedActiveTickers = orderedTickers[i]
          }
        }
      }
      if (DEBUG) console.log("Storing tickers in order:" + orderedActiveTickers)
      prefs.setCharPref("extensions.ADDON_ID.tickers_order", orderedActiveTickers) // Update list of tickers active in order in preferences
    }
  }

  // Live enable/disable ticker from options checkbox
  function toggleTicker(tickerId) {
    if ( getBooleanPreference('p' + tickerId) ) { // Enable Ticker
      var tickerData = loadTicker(tickerId)
      if (tickerData.enabled) orderedTickers.push(tickerId)
      storeTickersOrder()
    } else if ( (tickers[tickerId] != null) && (tickers[tickerId].enabled)) { // Disable Ticker if it exists
      tickers[tickerId].enabled = false
      updateTickerConfiguration(tickers[tickerId])
      for (var position in orderedTickers) {
        if (orderedTickers[position] == tickerId) {
          orderedTickers.splice(position, 1) // Remove the ticker completely from the array with reordering
          break
        }
      }
      tickers[tickerId] = null
      storeTickersOrder()
    }
  }

  function updateTickerConfiguration(tickerData) {
    tickersFrame.postMessage({
      "type": "updateTickerConfiguration",
      "data": tickerData
    }, tickersFrame.url);
  }

  function fetchURLData(e) {
    if (DEBUG) console.log("Request received from frame:" + JSON.stringify(e))
    if (e.data == "undefined" || e.data.id == "undefined" ||
        e.data.url == "undefined" || e.data.jsonPath == "undefined") {
      return
    }
    var id = e.data.id
    var url = e.data.url
    var jsonPath = JSON.parse(e.data.jsonPath)
    if (DEBUG) console.log("Requesting JSON data from " + url)
    Request({
      url: url,
      onComplete: function (response) {
        if ((response != null) && (response.json != null)) {
          if (DEBUG) console.log("Data received, searching in document for path:" + jsonPath)
          var price = response.json
          for (var i = 0; i < jsonPath.length; i++) { // Parse JSON path
            if (typeof price[jsonPath[i]] == "undefined") {
              if (DEBUG) console.log("BitcoinPriceTicker error loading ticker " + id + ". URL is not correctly responding:" + url)
              return
            }
            price = price[jsonPath[i]]
          }
          if (DEBUG) console.log("Price received and parsed: " + price)
          e.source.postMessage({
            "type": "updateTickerModelPrice",
            "data": {
              "id": id,
              "price": price
            }
          }, e.origin)
        }
      }
    }).get()
  }

  function updateActiveTickersSharedStyle() {
    for (tickerId in tickers) {
      if (tickers[tickerId]) {
        loadTicker(tickerId) // Update configuration
      }
    }
  }

  function showAddonUpdateDocument() {
    tabs.open(ADDON_UPDATE_DOCUMENT_URL)
  }

  function showAddonUpdate(version) {
    try {
      if (( ! getBooleanPreference("show-updates")) || // Requested to not show updates
          (prefs.getCharPref("extensions.ADDON_ID.version") == version)) { // Not updated
        return
      }
    } catch (e) {} // There is no addon version set yet
    if (! DEBUG) setTimeout(showAddonUpdateDocument, 5000) // Showing update webpage
    prefs.setCharPref("extensions.ADDON_ID.version", version) // Update version number in preferences
  }

  function loadProvidersData() {
    var url = DATA_PROVIDERS_URL
    if (DEBUG) console.log("Requesting JSON data from " + DATA_PROVIDERS_URL)
    Request({
      url: url,
      onComplete: function (response) {
        if ((response != null) && (response.json != null)) {
          if (DEBUG) console.log("Data received from data providers JSON configuration")
          tickers = response.json
          if (Object.keys(tickers).length == 0) {
            if (DEBUG) console.log("Error: No ticker configuration found in JSON configuration received from server:"+url)
            return
          }
          initAfterLoad()
        }
      }
    }).get()
  }

  function initAfterLoad() {
    loadTickersInOrder()
    registerEvents()
  }

  var tickersFrame = ui.Frame({
    url: './index.html',
    onMessage: fetchURLData
  })

  tickersFrame.on("ready", loadProvidersData) // When the presenter is ready load config data and tickers

  var toolbar = ui.Toolbar({
    title: 'Bitcoin Price Ticker',
    items: [tickersFrame]
  })
/*
  Feature disabled until refactored

  var calculateSlopeAndTrend = function(last_price, price, trend) {
    var slope = (last_price>0) ? price/last_price - 1 : 0;
    var label_slope = '\u2194';
    var st = price;
    var bt = 0;
    if (slope>=0.001) {
      label_slope = (slope>=0.01) ? '\u219f' : '\u2191';
    }
    else if (slope<=-0.001) {
      label_slope = (slope<=-0.01) ? '\u21a1' : '\u2193';
    }
    // Double Exponential Smoothing
    // http://en.wikipedia.org/wiki/Exponential_smoothing
    // magic numbers, from experiments in spreadsheet:
    //   alpha = 0.05 and beta=0.1
    if (last_price != 0) {
      if (trend[0] == 0) {
        st = price;
        bt = price - last_price;
      }
      else {
        st = .05* price + .95 * (trend[0] + trend[1]);
        bt = .1 * (st - trend[0]) + .9 * trend[1];
      }
    }
    var label_trend = '\u21d4'; // ⇔
    var change = 10000*bt/st;
    if (change>=2.5) {
      label_trend = '\u21d1'; // ⇑
    }
    else if (change>=1.0) {
      label_trend = '\u21d7'; // ⇗
    }
    else if (change<=-2.5) { // ⇓
      label_trend = '\u21d3';
    }
    else if (change<=-1.0) { // ⇘
      label_trend = '\u21d8';
    }
    return {
        trend: [st, bt],
        label_trend: label_trend,
        label_slope: label_slope
    };
  }

    price = price[ticker.json_path[i]];
    var trends = calculateSlopeAndTrend(ticker.last, price, ticker.trend);
    ticker.trend = trends.trend;
    label_trend = trends.label_trend;
    label_slope = trends.label_slope;
    var round = calculateRoundFactor(price);
    var change = Math.round(1000000*ticker.trend[1]/ticker.trend[0])/100;
    var last_ticker_price = Math.round(ticker.last * round.factor) / round.factor;
    last_ticker_price = (round.size > 1) && (last_ticker_price > 0) ? last_ticker_price.toFixed(round.size) : last_ticker_price;
    ticker.tooltip = ticker.label + " -- previous: "
        + labelWithCurrency(last_ticker_price, currency)
        + " -- trend: " + ((change>0) ? "+" : "") + change;
    ticker.last = price;
    price = Math.round(price * round.factor) / round.factor;
    price = (round.size > 1) && (price > 0) ? price.toFixed(round.size) : price;
    latest_content = labelWithCurrency(price, currency);
    if (getBooleanPreference("show-short-trend")) {
      latest_content = label_slope + latest_content;
    }
    if (getBooleanPreference("show-long-trend")) {
      latest_content = label_trend + latest_content;
    }
  ticker.port.emit("updateContent", latest_content);
  updateTickerStyle();
  */

  // Register general settings events
  Preferences.on('defaultFontSize', updateActiveTickersSharedStyle);
  Preferences.on('Timer', updateActiveTickersSharedStyle);
  Preferences.on('gold-background', updateActiveTickersSharedStyle);
  Preferences.on('silver-background', updateActiveTickersSharedStyle);
  Preferences.on('other-background', updateActiveTickersSharedStyle);
  // Preferences.on('show-long-trend', updateAllTickers);
  // Preferences.on('show-short-trend', updateAllTickers);
  Preferences.on('show-currency-label', updateActiveTickersSharedStyle);

  function registerTickerEvents(tickerId) {
    Preferences.on('p' + tickerId, function() { // Create event to enable/disable of tickers
      toggleTicker(tickerId)
    })
    // Create events to update ticker when a particular option is changed
    Preferences.on('p' + tickerId + 'Color', function() {
      if (tickers[tickerId] != null) {
        tickers[tickerId].color = getStringPreference("p" + tickerId + "Color")
        updateTickerConfiguration(tickers[tickerId])
      }
    })
  }

  function registerEvents() {
    for (tickerId in tickers) {
      registerTickerEvents(tickerId)
    }
  }
  Preferences.on('infoButton', showAddonUpdateDocument)
  // Check updated version
  AddonManager.getAddonByID(ADDON_ID, function(addon) {
    showAddonUpdate(addon.version);
  })
};
<commit_msg>Fix bug on preference change<commit_after>const DEBUG = true
const DATA_PROVIDERS_URL = "https://raw.githubusercontent.com/neoranga55/bitcoin-price-ticker/refactor-to-use-firefox-frames/data/data-providers.json"
const ADDON_UPDATE_DOCUMENT_URL = "http://neoranga55.github.io/bitcoin-price-ticker/"

// The main module of the Add-on.
var ui = require('sdk/ui')
const {Cc, Ci, Cu} = require("chrome")
Cu.import("resource://gre/modules/AddonManager.jsm") // Addon Manager required to know addon version
const setTimeout = require("sdk/timers").setTimeout
const ADDON_ID = "jid0-ziK34XHkBWB9ezxd4l9Q1yC7RP0@jetpack"
const DEFAULT_REFRESH_RATE = 60
const DEFAULT_FONT_SIZE = 14

var Preferences = require('sdk/simple-prefs');
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.ADDON_ID.");
var Request = require("sdk/request").Request;
var tabs = require("sdk/tabs");

var orderedTickers = new Array()
// Ids of all tickers available. Ticker configuration for each ID is in file data/js/providers.js
var tickers = {} // Store all tickers here

exports.main = function() {

  function getPreference(prefName, type) {
    if (typeof Preferences.prefs[prefName] == "undefined") {
      if (DEBUG) console.log("bitcoin-price-ticker addon error: " + prefName + " preference is not defined")
      switch (type) {
        case "boolean":
          return false
        case "integer":
          return -1
        case "string":
          return ""
        default:
          return null
      }
    }
    return Preferences.prefs[prefName]
  }

  function getBooleanPreference(prefName) {
    return getPreference(prefName, "boolean")
  }

  function getIntegerPreference(prefName) {
    return getPreference(prefName, "integer")
  }

  function getStringPreference(prefName) {
    return getPreference(prefName, "string")
  }

  function getBackgroundColor(id) {
    var lowId = id.toLowerCase()
    var otherBgCryptos = [ 'dogecoin', 'worldcoin', 'namecoin', 'auroracoin', 'blackcoin', 'nxt',
      'bitshares', 'ripple', 'maidsafe', 'bitcoindark', 'monero', 'dash', 'burst' ]
    for (var i in otherBgCryptos) {
      if (lowId.indexOf(otherBgCryptos[i]) != -1) {  // Alt-coin
        if (getBooleanPreference("other-background")) {
          return otherBgCryptos[i]
        }
      }
    }
    if (lowId.indexOf("litecoin") != -1) {
      if (getBooleanPreference("silver-background")) { // Currency is Litecoin
        return "silver"
      }
    } else if (getBooleanPreference("gold-background")) { // Currency is Bitcoin
      return "gold"
    }
    return null
  }

  function getTickerConfigurationData(tickerId) {
    var fontSize = getIntegerPreference("defaultFontSize");
    if (fontSize <= 0) {
      fontSize = DEFAULT_FONT_SIZE
    }
    var refreshRate = getIntegerPreference("Timer");
    if (refreshRate < 1) {
      refreshRate = DEFAULT_REFRESH_RATE;
    }
    var tickerData = {
      id: tickerId,
      enabled: getBooleanPreference("p" + tickerId),
      currencyPosition: getStringPreference("show-currency-label"),
      color: getStringPreference("p" + tickerId + "Color"),
      fontSize: fontSize,
      background: getBackgroundColor(tickerId),
      updateInterval: refreshRate
    }
    return tickerData
  }

  function loadTicker(tickerId) {
    var tickerData = getTickerConfigurationData(tickerId)
    tickers[tickerId] = tickerData
    updateTickerConfiguration(tickerData)
    return tickerData
  }

  // Use tickers enabled in preferences to load in that order regardless of stored order
  function loadDefaultTickers() {
    for (var tickerId in tickers) {
      if ( getBooleanPreference('p' + tickerId) ) { // Create Ticker
        var tickerData = loadTicker(tickerId)
        if (tickerData.enabled) orderedTickers.push(tickerId)
      }
    }
    if ((orderedTickers != null) && (orderedTickers.length > 0)) {
      storeTickersOrder()
    }
  }

  // Load the order of the tickers and simultaneously create them
  function loadTickersInOrder() {
    var orderedActiveTickers = ""
    try {
      orderedActiveTickers = prefs.getCharPref("extensions.ADDON_ID.tickers_order")
      if (orderedActiveTickers.length < 1) { // There is no order of tickers in addon set yet
        loadDefaultTickers()
        return
      }
      var listOrderedTickers = orderedActiveTickers.split(',')
      if (listOrderedTickers.length < 1) { // There is no order of tickers in addon set yet
        loadDefaultTickers()
        return
      }
      for (var i in listOrderedTickers) {
        var tickerData = loadTicker(listOrderedTickers[i])
        if (tickerData.enabled) orderedTickers.push(tickerId)
      }
    } catch (e) { // There is no order of tickers in addon set yet
      loadDefaultTickers()
    }
  }

  // Store the order of the active tickers
  function storeTickersOrder() {
    if ((orderedTickers == null) || (orderedTickers.length == 0)) {
      loadDefaultTickers()
    } else {
      var orderedActiveTickers = ""
      if ((orderedTickers != null) && (orderedTickers.length > 0)) {
        for (var i in orderedTickers) { // Traverse skipping empty
          if (orderedActiveTickers.length > 0) {
            orderedActiveTickers += "," + orderedTickers[i]
          } else {
            orderedActiveTickers = orderedTickers[i]
          }
        }
      }
      if (DEBUG) console.log("Storing tickers in order:" + orderedActiveTickers)
      prefs.setCharPref("extensions.ADDON_ID.tickers_order", orderedActiveTickers) // Update list of tickers active in order in preferences
    }
  }

  // Live enable/disable ticker from options checkbox
  function toggleTicker(tickerId) {
    if ( getBooleanPreference('p' + tickerId) ) { // Enable Ticker
      var tickerData = loadTicker(tickerId)
      if (tickerData.enabled) orderedTickers.push(tickerId)
      storeTickersOrder()
    } else if ( (tickers[tickerId] != null) && (tickers[tickerId].enabled)) { // Disable Ticker if it exists
      tickers[tickerId].enabled = false
      updateTickerConfiguration(tickers[tickerId])
      for (var position in orderedTickers) {
        if (orderedTickers[position] == tickerId) {
          orderedTickers.splice(position, 1) // Remove the ticker completely from the array with reordering
          break
        }
      }
      tickers[tickerId] = null
      storeTickersOrder()
    }
  }

  function updateTickerConfiguration(tickerData) {
    tickersFrame.postMessage({
      "type": "updateTickerConfiguration",
      "data": tickerData
    }, tickersFrame.url);
  }

  function fetchURLData(e) {
    if (DEBUG) console.log("Request received from frame:" + JSON.stringify(e))
    if (e.data == "undefined" || e.data.id == "undefined" ||
        e.data.url == "undefined" || e.data.jsonPath == "undefined") {
      return
    }
    var id = e.data.id
    var url = e.data.url
    var jsonPath = JSON.parse(e.data.jsonPath)
    if (DEBUG) console.log("Requesting JSON data from " + url)
    Request({
      url: url,
      onComplete: function (response) {
        if ((response != null) && (response.json != null)) {
          if (DEBUG) console.log("Data received, searching in document for path:" + jsonPath)
          var price = response.json
          for (var i = 0; i < jsonPath.length; i++) { // Parse JSON path
            if (typeof price[jsonPath[i]] == "undefined") {
              if (DEBUG) console.log("BitcoinPriceTicker error loading ticker " + id + ". URL is not correctly responding:" + url)
              return
            }
            price = price[jsonPath[i]]
          }
          if (DEBUG) console.log("Price received and parsed: " + price)
          e.source.postMessage({
            "type": "updateTickerModelPrice",
            "data": {
              "id": id,
              "price": price
            }
          }, e.origin)
        }
      }
    }).get()
  }

  function updateActiveTickersSharedStyle() {
    for (tickerId in tickers) {
      if (tickers[tickerId] && tickers[tickerId].enabled) {
        loadTicker(tickerId) // Update configuration
      }
    }
  }

  function showAddonUpdateDocument() {
    tabs.open(ADDON_UPDATE_DOCUMENT_URL)
  }

  function showAddonUpdate(version) {
    try {
      if (( ! getBooleanPreference("show-updates")) || // Requested to not show updates
          (prefs.getCharPref("extensions.ADDON_ID.version") == version)) { // Not updated
        return
      }
    } catch (e) {} // There is no addon version set yet
    if (! DEBUG) setTimeout(showAddonUpdateDocument, 5000) // Showing update webpage
    prefs.setCharPref("extensions.ADDON_ID.version", version) // Update version number in preferences
  }

  function loadProvidersData() {
    var url = DATA_PROVIDERS_URL
    if (DEBUG) console.log("Requesting JSON data from " + DATA_PROVIDERS_URL)
    Request({
      url: url,
      onComplete: function (response) {
        if ((response != null) && (response.json != null)) {
          if (DEBUG) console.log("Data received from data providers JSON configuration")
          tickers = response.json
          if (Object.keys(tickers).length == 0) {
            if (DEBUG) console.log("Error: No ticker configuration found in JSON configuration received from server:"+url)
            return
          }
          initAfterLoad()
        }
      }
    }).get()
  }

  function initAfterLoad() {
    loadTickersInOrder()
    registerEvents()
  }

  var tickersFrame = ui.Frame({
    url: './index.html',
    onMessage: fetchURLData
  })

  tickersFrame.on("ready", loadProvidersData) // When the presenter is ready load config data and tickers

  var toolbar = ui.Toolbar({
    title: 'Bitcoin Price Ticker',
    items: [tickersFrame]
  })
/*
  Feature disabled until refactored

  var calculateSlopeAndTrend = function(last_price, price, trend) {
    var slope = (last_price>0) ? price/last_price - 1 : 0;
    var label_slope = '\u2194';
    var st = price;
    var bt = 0;
    if (slope>=0.001) {
      label_slope = (slope>=0.01) ? '\u219f' : '\u2191';
    }
    else if (slope<=-0.001) {
      label_slope = (slope<=-0.01) ? '\u21a1' : '\u2193';
    }
    // Double Exponential Smoothing
    // http://en.wikipedia.org/wiki/Exponential_smoothing
    // magic numbers, from experiments in spreadsheet:
    //   alpha = 0.05 and beta=0.1
    if (last_price != 0) {
      if (trend[0] == 0) {
        st = price;
        bt = price - last_price;
      }
      else {
        st = .05* price + .95 * (trend[0] + trend[1]);
        bt = .1 * (st - trend[0]) + .9 * trend[1];
      }
    }
    var label_trend = '\u21d4'; // ⇔
    var change = 10000*bt/st;
    if (change>=2.5) {
      label_trend = '\u21d1'; // ⇑
    }
    else if (change>=1.0) {
      label_trend = '\u21d7'; // ⇗
    }
    else if (change<=-2.5) { // ⇓
      label_trend = '\u21d3';
    }
    else if (change<=-1.0) { // ⇘
      label_trend = '\u21d8';
    }
    return {
        trend: [st, bt],
        label_trend: label_trend,
        label_slope: label_slope
    };
  }

    price = price[ticker.json_path[i]];
    var trends = calculateSlopeAndTrend(ticker.last, price, ticker.trend);
    ticker.trend = trends.trend;
    label_trend = trends.label_trend;
    label_slope = trends.label_slope;
    var round = calculateRoundFactor(price);
    var change = Math.round(1000000*ticker.trend[1]/ticker.trend[0])/100;
    var last_ticker_price = Math.round(ticker.last * round.factor) / round.factor;
    last_ticker_price = (round.size > 1) && (last_ticker_price > 0) ? last_ticker_price.toFixed(round.size) : last_ticker_price;
    ticker.tooltip = ticker.label + " -- previous: "
        + labelWithCurrency(last_ticker_price, currency)
        + " -- trend: " + ((change>0) ? "+" : "") + change;
    ticker.last = price;
    price = Math.round(price * round.factor) / round.factor;
    price = (round.size > 1) && (price > 0) ? price.toFixed(round.size) : price;
    latest_content = labelWithCurrency(price, currency);
    if (getBooleanPreference("show-short-trend")) {
      latest_content = label_slope + latest_content;
    }
    if (getBooleanPreference("show-long-trend")) {
      latest_content = label_trend + latest_content;
    }
  ticker.port.emit("updateContent", latest_content);
  updateTickerStyle();
  */

  // Register general settings events
  Preferences.on('defaultFontSize', updateActiveTickersSharedStyle);
  Preferences.on('Timer', updateActiveTickersSharedStyle);
  Preferences.on('gold-background', updateActiveTickersSharedStyle);
  Preferences.on('silver-background', updateActiveTickersSharedStyle);
  Preferences.on('other-background', updateActiveTickersSharedStyle);
  // Preferences.on('show-long-trend', updateAllTickers);
  // Preferences.on('show-short-trend', updateAllTickers);
  Preferences.on('show-currency-label', updateActiveTickersSharedStyle);

  function registerTickerEvents(tickerId) {
    Preferences.on('p' + tickerId, function() { // Create event to enable/disable of tickers
      toggleTicker(tickerId)
    })
    // Create events to update ticker when a particular option is changed
    Preferences.on('p' + tickerId + 'Color', function() {
      if (tickers[tickerId] != null) {
        tickers[tickerId].color = getStringPreference("p" + tickerId + "Color")
        updateTickerConfiguration(tickers[tickerId])
      }
    })
  }

  function registerEvents() {
    for (tickerId in tickers) {
      registerTickerEvents(tickerId)
    }
  }
  Preferences.on('infoButton', showAddonUpdateDocument)
  // Check updated version
  AddonManager.getAddonByID(ADDON_ID, function(addon) {
    showAddonUpdate(addon.version);
  })
};
<|endoftext|>


<commit_before>(function ($) {
  // register namespace
  $.extend(true, window, {
    "Slick": {
      "RowMoveManager": RowMoveManager
    }
  });

  function RowMoveManager() {
    var _grid;
    var _canvas;
    var _dragging;
    var _self = this;
    var _handler = new Slick.EventHandler();

    function init(grid) {
      _grid = grid;
      _canvas = _grid.getCanvasNode();
      _handler
        .subscribe(_grid.onDragInit, handleDragInit)
        .subscribe(_grid.onDragStart, handleDragStart)
        .subscribe(_grid.onDrag, handleDrag)
        .subscribe(_grid.onDragEnd, handleDragEnd);
    }

    function destroy() {
      _handler.unsubscribeAll();
    }

    function handleDragInit(e, dd) {
      // prevent the grid from cancelling drag'n'drop by default
      e.stopImmediatePropagation();
    }

    function handleDragStart(e, dd) {
      var cell = _grid.getCellFromEvent(e);
      if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) {
        return false;
      }

      _dragging = true;
      e.stopImmediatePropagation();

      var selectedRows = _grid.getSelectedRows();

      if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) {
        selectedRows = [cell.row];
        _grid.setSelectedRows(selectedRows);
      }

      var rowHeight = _grid.getOptions().rowHeight;

      dd.selectedRows = selectedRows;

      dd.selectionProxy = $("
")
          .css("position", "absolute")
          .css("zIndex", "99999")
          .css("width", $(_canvas).innerWidth())
          .css("height", rowHeight * selectedRows.length)
          .appendTo(_canvas);

      dd.guide = $("
")
          .css("position", "absolute")
          .css("zIndex", "99998")
          .css("width", $(_canvas).innerWidth())
          .css("top", -1000)
          .appendTo(_canvas);

      dd.insertBefore = -1;
    }

    function handleDrag(e, dd) {
      if (!_dragging) {
        return;
      }

      e.stopImmediatePropagation();

      var top = e.pageY - $(_canvas).offset().top;
      dd.selectionProxy.css("top", top - 5);

      var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength()));
      if (insertBefore !== dd.insertBefore) {
        var eventData = {
          "rows": dd.selectedRows,
          "insertBefore": insertBefore
        };

        if (_self.onBeforeMoveRows.notify(eventData) === false) {
          dd.guide.css("top", -1000);
          dd.canMove = false;
        } else {
          dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight);
          dd.canMove = true;
        }

        dd.insertBefore = insertBefore;
      }
    }

    function handleDragEnd(e, dd) {
      if (!_dragging) {
        return;
      }
      _dragging = false;
      e.stopImmediatePropagation();

      dd.guide.remove();
      dd.selectionProxy.remove();

      if (dd.canMove) {
        var eventData = {
          "rows": dd.selectedRows,
          "insertBefore": dd.insertBefore
        };
        // TODO:  _grid.remapCellCssClasses ?
        _self.onMoveRows.notify(eventData);
      }
    }

    $.extend(this, {
      "onBeforeMoveRows": new Slick.Event(),
      "onMoveRows": new Slick.Event(),

      "init": init,
      "destroy": destroy
    });
  }
})(jQuery);<commit_msg>Bump up slickgrid version.<commit_after>(function ($) {
  // register namespace
  $.extend(true, window, {
    "Slick": {
      "RowMoveManager": RowMoveManager
    }
  });

  function RowMoveManager(options) {
    var _grid;
    var _canvas;
    var _dragging;
    var _self = this;
    var _handler = new Slick.EventHandler();
    var _defaults = {
      cancelEditOnDrag: false
    };

    function init(grid) {
      options = $.extend(true, {}, _defaults, options);
      _grid = grid;
      _canvas = _grid.getCanvasNode();
      _handler
        .subscribe(_grid.onDragInit, handleDragInit)
        .subscribe(_grid.onDragStart, handleDragStart)
        .subscribe(_grid.onDrag, handleDrag)
        .subscribe(_grid.onDragEnd, handleDragEnd);
    }

    function destroy() {
      _handler.unsubscribeAll();
    }

    function handleDragInit(e, dd) {
      // prevent the grid from cancelling drag'n'drop by default
      e.stopImmediatePropagation();
    }

    function handleDragStart(e, dd) {
      var cell = _grid.getCellFromEvent(e);

      if (options.cancelEditOnDrag && _grid.getEditorLock().isActive()) {
        _grid.getEditorLock().cancelCurrentEdit();
      }

      if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) {
        return false;
      }

      _dragging = true;
      e.stopImmediatePropagation();

      var selectedRows = _grid.getSelectedRows();

      if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) {
        selectedRows = [cell.row];
        _grid.setSelectedRows(selectedRows);
      }

      var rowHeight = _grid.getOptions().rowHeight;

      dd.selectedRows = selectedRows;

      dd.selectionProxy = $("
")
          .css("position", "absolute")
          .css("zIndex", "99999")
          .css("width", $(_canvas).innerWidth())
          .css("height", rowHeight * selectedRows.length)
          .appendTo(_canvas);

      dd.guide = $("
")
          .css("position", "absolute")
          .css("zIndex", "99998")
          .css("width", $(_canvas).innerWidth())
          .css("top", -1000)
          .appendTo(_canvas);

      dd.insertBefore = -1;
    }

    function handleDrag(e, dd) {
      if (!_dragging) {
        return;
      }

      e.stopImmediatePropagation();

      var top = e.pageY - $(_canvas).offset().top;
      dd.selectionProxy.css("top", top - 5);

      var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength()));
      if (insertBefore !== dd.insertBefore) {
        var eventData = {
          "rows": dd.selectedRows,
          "insertBefore": insertBefore
        };

        if (_self.onBeforeMoveRows.notify(eventData) === false) {
          dd.guide.css("top", -1000);
          dd.canMove = false;
        } else {
          dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight);
          dd.canMove = true;
        }

        dd.insertBefore = insertBefore;
      }
    }

    function handleDragEnd(e, dd) {
      if (!_dragging) {
        return;
      }
      _dragging = false;
      e.stopImmediatePropagation();

      dd.guide.remove();
      dd.selectionProxy.remove();

      if (dd.canMove) {
        var eventData = {
          "rows": dd.selectedRows,
          "insertBefore": dd.insertBefore
        };
        // TODO:  _grid.remapCellCssClasses ?
        _self.onMoveRows.notify(eventData);
      }
    }

    $.extend(this, {
      "onBeforeMoveRows": new Slick.Event(),
      "onMoveRows": new Slick.Event(),

      "init": init,
      "destroy": destroy
    });
  }
})(jQuery);<|endoftext|>


<commit_before>'use strict'                                 // eslint-disable-line

const exec   = require('child_process').exec // https://nodejs.org/api/child_process.html
const chalk  = require('chalk')              // https://github.com/chalk/chalk
const fs     = require('fs')                 // https://nodejs.org/api/fs.html
const semver = require('semver')             // https://www.npmjs.com/package/semver

const configFileName = '.reliantrc'
const isWindows      = /^win/.test(process.platform)
const passSymbol     = isWindows ? ' ' : '\u2713'
const failSymbol     = isWindows ? 'x' : '\u2717'

module.exports = function reliant(options) {
    // Default reporter - console logs results
    function defaultReporter(results, error) {
        const pluralise = (count, singular, plural) => `${count} ${count === 1 ? singular : plural}`

        // Output title
        console.log('Reliant\n=======\n')

        // Check if results is an error
        if (error) {
            // Just display error
            console.log('Sorry, something went wrong...\n')
            console.log(chalk.red(`${results}\n`))
        } else {
            // Build summary
            const summary = results
                .map(result => result.pass ? chalk.green(`${passSymbol} ${chalk.bold(result.name)}`) : chalk.red(`${failSymbol} ${chalk.bold(result.name)}`))

            // Build issues
            const issues = results
                .filter(result => !result.pass)
                .map(result => {
                    if (result.error) {
                        return `+ ${chalk.bold(result.name)} requires ${chalk.bold(result.version.required)} but errored with ${chalk.bold.red(result.error)}`
                    } else {
                        return `+ ${chalk.bold(result.name)} requires ${chalk.bold(result.version.required)} but found ${chalk.bold.red(result.version.found)}`
                    }
                })

            // Output report
            console.log(`Summary:\n${summary.join('\n')}`)
            console.log(`\n${pluralise(results.length, 'test', 'tests')}, ${pluralise(results.length - issues.length, 'pass', 'passes')}, ${pluralise(issues.length, 'failure', 'failures')}\n`)

            // Got issues?
            if (issues.length) {
                console.log(`Issues:\n${issues.join('\n')}\n`)
            } else {
                console.log('Horay! No issues!\n')
            }
        }
    }

    // Tests environment against rules
    function run(rules) {
        // Create an array of Promises
        const tests = rules.map(rule => {
            return new Promise(resolve => {
                // Execute command
                exec(rule.cmd, (error, stdout, stderr) => {
                    // Resolve the Promise and return a details object
                    resolve({
                        name: rule.name,
                        cmd: rule.cmd,
                        version: {
                            required: rule.version,
                            found: stdout.replace(/\r?\n|\r/g, '')
                        },
                        error: error !== null ? stderr.replace(/\r?\n|\r$/, '') : null,
                        pass: semver.satisfies(stdout, rule.version)
                    })
                })
            })
        })

        // Wait for all promises to be resolved before continuing
        return Promise.all(tests)
    }

    // Extends default options with any custom options
    options = Object.assign({}, { reporter: defaultReporter }, options)

    // Read .envrc file
    fs.readFile(configFileName, 'utf8', function (err, rules) {
        if (err) {
            throw new Error(`Could not load ${configFileName} file.`)
        }

        // Try to parse file contents as JSON
        try {
            rules = JSON.parse(rules)
        } catch (e) {
            throw new Error(`The ${configFileName} file could not be parsed. Check that it is valid JSON.`)
        }

        // Run
        run(rules)
            .then(results => {
                options.reporter(results, false)

                // If we have any fails, exit with
                if (results.filter(result => !result.pass).length > 0) {
                    // Failure
                    process.exit(1)
                } else {
                    // Success
                    process.exit()
                }
            }).catch(error => {
                options.reporter(error, true)
            })
    })
}
<commit_msg>No issues message tweak<commit_after>'use strict'                                 // eslint-disable-line

const exec   = require('child_process').exec // https://nodejs.org/api/child_process.html
const chalk  = require('chalk')              // https://github.com/chalk/chalk
const fs     = require('fs')                 // https://nodejs.org/api/fs.html
const semver = require('semver')             // https://www.npmjs.com/package/semver

const configFileName = '.reliantrc'
const isWindows      = /^win/.test(process.platform)
const passSymbol     = isWindows ? ' ' : '\u2713'
const failSymbol     = isWindows ? 'x' : '\u2717'

module.exports = function reliant(options) {
    // Default reporter - console logs results
    function defaultReporter(results, error) {
        const pluralise = (count, singular, plural) => `${count} ${count === 1 ? singular : plural}`

        // Output title
        console.log('Reliant\n=======\n')

        // Check if results is an error
        if (error) {
            // Just display error
            console.log('Sorry, something went wrong...\n')
            console.log(chalk.red(`${results}\n`))
        } else {
            // Build summary
            const summary = results
                .map(result => result.pass ? chalk.green(`${passSymbol} ${chalk.bold(result.name)}`) : chalk.red(`${failSymbol} ${chalk.bold(result.name)}`))

            // Build issues
            const issues = results
                .filter(result => !result.pass)
                .map(result => {
                    if (result.error) {
                        return `+ ${chalk.bold(result.name)} requires ${chalk.bold(result.version.required)} but errored with ${chalk.bold.red(result.error)}`
                    } else {
                        return `+ ${chalk.bold(result.name)} requires ${chalk.bold(result.version.required)} but found ${chalk.bold.red(result.version.found)}`
                    }
                })

            // Output report
            console.log(`Summary:\n${summary.join('\n')}`)
            console.log(`\n${pluralise(results.length, 'test', 'tests')}, ${pluralise(results.length - issues.length, 'pass', 'passes')}, ${pluralise(issues.length, 'failure', 'failures')}\n`)

            // Got issues?
            if (issues.length) {
                console.log(`Issues:\n${issues.join('\n')}\n`)
            } else {
                console.log('No issues!\n')
            }
        }
    }

    // Tests environment against rules
    function run(rules) {
        // Create an array of Promises
        const tests = rules.map(rule => {
            return new Promise(resolve => {
                // Execute command
                exec(rule.cmd, (error, stdout, stderr) => {
                    // Resolve the Promise and return a details object
                    resolve({
                        name: rule.name,
                        cmd: rule.cmd,
                        version: {
                            required: rule.version,
                            found: stdout.replace(/\r?\n|\r/g, '')
                        },
                        error: error !== null ? stderr.replace(/\r?\n|\r$/, '') : null,
                        pass: semver.satisfies(stdout, rule.version)
                    })
                })
            })
        })

        // Wait for all promises to be resolved before continuing
        return Promise.all(tests)
    }

    // Extends default options with any custom options
    options = Object.assign({}, { reporter: defaultReporter }, options)

    // Read .envrc file
    fs.readFile(configFileName, 'utf8', function (err, rules) {
        if (err) {
            throw new Error(`Could not load ${configFileName} file.`)
        }

        // Try to parse file contents as JSON
        try {
            rules = JSON.parse(rules)
        } catch (e) {
            throw new Error(`The ${configFileName} file could not be parsed. Check that it is valid JSON.`)
        }

        // Run
        run(rules)
            .then(results => {
                options.reporter(results, false)

                // If we have any fails, exit with
                if (results.filter(result => !result.pass).length > 0) {
                    // Failure
                    process.exit(1)
                } else {
                    // Success
                    process.exit()
                }
            }).catch(error => {
                options.reporter(error, true)
            })
    })
}
<|endoftext|>


<commit_before>var express = require('express'),
    app = express(),
    router = express.Router(),
    config = require('./config'),
    g = require('./src/g');

router
    .get('/:path(*)', function (req, res) {
        var path = req.params.path;

        if (g.isCached(path) && g.isCacheFresh(path, config.cacheExp)) {
            g.getCacheAndOutput(res, path);
        }
        else {
            g.writeCacheAndOutput(res, path);
        }

    });

app
    .use('/', router)
    .listen(config.port, function() {
        console.log('Ghiaccio started.');
    });
<commit_msg>port fix<commit_after>var express = require('express'),
    app = express(),
    router = express.Router(),
    config = require('./config'),
    g = require('./src/g');

router
    .get('/:path(*)', function (req, res) {
        var path = req.params.path;

        if (g.isCached(path) && g.isCacheFresh(path, config.cacheExp)) {
            g.getCacheAndOutput(res, path);
        }
        else {
            g.writeCacheAndOutput(res, path);
        }

    });

app
    .use('/', router)
    .listen(process.env.PORT || config.port, function() {
        console.log('Ghiaccio started.');
    });
<|endoftext|>


<commit_before>var Ipc = require('ipc');
var BrowserWindow = require('browser-window');

/**
 * Redirect panel messages to its registered windows.
 */
var Panel = {};
var _panel2windows = {};
var _panel2argv = {};

Panel.templateUrl = 'editor-framework://static/window.html';

var _dock = function ( panelID, win ) {
    // Editor.info('%s dock to %s', panelID, win.name ); // DEBUG

    var editorWin = _panel2windows[panelID];

    // if we found same panel dock in different place
    if ( editorWin && editorWin !== win ) {
        // TODO: should we report error ????
    }

    _panel2windows[panelID] = win;
};

var _undock = function ( panelID ) {
    var editorWin = _panel2windows[panelID];
    // Editor.info('%s undock from %s', panelID, editorWin.name ); // DEBUG

    if ( editorWin ) {
        editorWin.sendToPage( 'panel:undock', panelID );
        delete _panel2windows[panelID];
        return editorWin;
    }
    return null;
};

var _saveLayout = function ( editorWin, panelID ) {
    // save standalone panel's layout
    if ( !editorWin.isMainWindow ) {
        var panelProfile = Editor.loadProfile( 'layout.' + panelID, 'local' );
        var winSize = editorWin.nativeWin.getContentSize();
        var winPos = editorWin.nativeWin.getPosition();

        panelProfile.x = winPos[0];
        panelProfile.y = winPos[1];
        panelProfile.width = winSize[0];
        panelProfile.height = winSize[1];
        panelProfile.save();
    }
};

//
Panel.open = function ( panelID, argv ) {
    var panelInfo = Editor.Package.panelInfo(panelID);
    if ( !panelInfo ) {
        Editor.error('Failed to open panel %s, panel info not found.', panelID);
        return;
    }

    _panel2argv[panelID] = argv;

    var editorWin = Panel.findWindow(panelID);
    if ( editorWin ) {
        // if we found the window, send panel:open to it
        Editor.sendToPanel( panelID, 'panel:open', argv );
        editorWin.show();
        editorWin.focus();
        return;
    }

    //
    var windowName = 'editor-window-' + new Date().getTime();
    var options = {
        'use-content-size': true,
        'width': parseInt(panelInfo.width),
        'height': parseInt(panelInfo.height),
        'min-width': parseInt(panelInfo['min-width']),
        'min-height': parseInt(panelInfo['min-height']),
        'max-width': parseInt(panelInfo['max-width']),
        'max-height': parseInt(panelInfo['max-height']),
    };

    // load layout-settings, and find windows by name
    var layoutProfile = Editor.loadProfile('layout.' + panelID, 'local' );
    if ( layoutProfile ) {
        options.x = parseInt(layoutProfile.x);
        options.y = parseInt(layoutProfile.y);
        options.width = parseInt(layoutProfile.width);
        options.height = parseInt(layoutProfile.height);
    }

    options['window-type'] = panelInfo.type || 'dockable';

    // NOTE: fixed-size window always use package.json settings
    if ( panelInfo.type === 'fixed-size' ) {
        options.width = parseInt(panelInfo.width);
        options.height = parseInt(panelInfo.height);
    }

    if ( isNaN(options.width) ) options.width = 800;
    if ( isNaN(options.height) ) options.height = 600;

    // create new window
    // DISABLE: currently, I don't want to support page
    // if ( panelInfo.page ) {
    //     url = panelInfo.page;
    // }

    //
    editorWin = new Editor.Window(windowName, options);
    _dock( panelID, editorWin );

    // BUG: https://github.com/atom/atom-shell/issues/1321
    editorWin.nativeWin.setContentSize( options.width, options.height );
    editorWin.nativeWin.setMenuBarVisibility(false);
    editorWin.load(Panel.templateUrl, {
        panelID: panelID
    });
    editorWin.focus();
};

Panel.close = function ( panelID ) {
    var editorWin = _undock(panelID);
    if ( editorWin ) {

        _saveLayout( editorWin, panelID );

        // check if we have other panels in the same window
        // if no panels left, we close the window
        var found = false;
        for ( var id in _panel2windows ) {
            if ( editorWin === _panel2windows[id] ) {
                found = true;
                break;
            }
        }

        // if not panel exists in this window, and it is not the main window, close it.
        if ( !found && !editorWin.isMainWindow ) {
            editorWin.close();
        }
    }
};

Panel.findWindow = function ( panelID ) {
    return _panel2windows[panelID];
};

Panel.findWindows = function (packageName) {
    var wins = [];

    for ( var p in _panel2windows ) {
        var pair = p.split('.');
        if ( pair.length !== 2 ) {
            continue;
        }

        var name = pair[1];
        if ( name === packageName ) {
            var editorWin = _panel2windows[p];
            if ( wins.indexOf (editorWin) === -1 )
                wins.push(editorWin);
        }
    }

    return wins;
};

Panel.findPanels = function ( packageName ) {
    var panelIDs = [];
    for ( var p in _panel2windows ) {
        var pair = p.split('.');
        if ( pair.length !== 2 ) {
            continue;
        }

        var name = pair[0];
        if ( name === packageName ) {
            panelIDs.push(pair);
        }
    }

    return panelIDs;
};

Panel.closeAll = function (packageName) {
    var panelIDs = Panel.findPanels(packageName);
    for (var i = 0; i < panelIDs.length; ++i) {
        Panel.close( panelIDs[i] );
    }
};

// NOTE: this only invoked in fire-window on-closed event
Panel._onWindowClosed = function ( editorWin ) {
    for ( var id in _panel2windows ) {
        var win = _panel2windows[id];
        if ( win === editorWin ) {
            _saveLayout( editorWin, id );
            delete _panel2windows[id];
        }
    }
};

// ========================================
// Ipc
// ========================================

Ipc.on('panel:query-info', function ( reply, panelID ) {
    if ( !panelID ) {
        Editor.error( 'Empty panelID' );
        reply();
        return;
    }

    // get panelInfo
    var panelInfo = Editor.Package.panelInfo(panelID);
    if ( panelInfo ) {
        // load profiles
        for ( var type in panelInfo.profiles ) {
            var profile = panelInfo.profiles[type];
            profile = Editor.loadProfile( panelID, type, profile );
            panelInfo.profiles[type] = profile;
        }
    }

    //
    reply(panelInfo);
});

Ipc.on('panel:ready', function ( panelID ) {
    var argv = _panel2argv[panelID];
    Editor.sendToPanel( panelID, 'panel:open', argv );
});

Ipc.on('panel:open', function ( panelID, argv ) {
    Panel.open( panelID, argv );
});

Ipc.on('panel:dock', function ( event, panelID ) {
    var browserWin = BrowserWindow.fromWebContents( event.sender );
    var editorWin = Editor.Window.find(browserWin);
    _dock( panelID, editorWin );
});

Ipc.on('panel:close', function ( panelID ) {
    Panel.close( panelID );
});

Ipc.on('panel:wait-for-close', function ( reply, panelID ) {
    Panel.close( panelID );
    reply();
});

//
Ipc.on('panel:save-profile', function ( panelID, type, panelProfile ) {
    var profile = Editor.loadProfile( panelID, type );
    if ( profile ) {
        profile.clear();
        Editor.JS.mixin(profile, panelProfile);
        profile.save();
    }
});

module.exports = Panel;
<commit_msg>make sure standalone panel have default min width and height<commit_after>var Ipc = require('ipc');
var BrowserWindow = require('browser-window');

/**
 * Redirect panel messages to its registered windows.
 */
var Panel = {};
var _panel2windows = {};
var _panel2argv = {};

Panel.templateUrl = 'editor-framework://static/window.html';

var _dock = function ( panelID, win ) {
    // Editor.info('%s dock to %s', panelID, win.name ); // DEBUG

    var editorWin = _panel2windows[panelID];

    // if we found same panel dock in different place
    if ( editorWin && editorWin !== win ) {
        // TODO: should we report error ????
    }

    _panel2windows[panelID] = win;
};

var _undock = function ( panelID ) {
    var editorWin = _panel2windows[panelID];
    // Editor.info('%s undock from %s', panelID, editorWin.name ); // DEBUG

    if ( editorWin ) {
        editorWin.sendToPage( 'panel:undock', panelID );
        delete _panel2windows[panelID];
        return editorWin;
    }
    return null;
};

var _saveLayout = function ( editorWin, panelID ) {
    // save standalone panel's layout
    if ( !editorWin.isMainWindow ) {
        var panelProfile = Editor.loadProfile( 'layout.' + panelID, 'local' );
        var winSize = editorWin.nativeWin.getContentSize();
        var winPos = editorWin.nativeWin.getPosition();

        panelProfile.x = winPos[0];
        panelProfile.y = winPos[1];
        panelProfile.width = winSize[0];
        panelProfile.height = winSize[1];
        panelProfile.save();
    }
};

//
Panel.open = function ( panelID, argv ) {
    var panelInfo = Editor.Package.panelInfo(panelID);
    if ( !panelInfo ) {
        Editor.error('Failed to open panel %s, panel info not found.', panelID);
        return;
    }

    _panel2argv[panelID] = argv;

    var editorWin = Panel.findWindow(panelID);
    if ( editorWin ) {
        // if we found the window, send panel:open to it
        Editor.sendToPanel( panelID, 'panel:open', argv );
        editorWin.show();
        editorWin.focus();
        return;
    }

    //
    var windowName = 'editor-window-' + new Date().getTime();
    var windowOptions = {
        'use-content-size': true,
        'width': parseInt(panelInfo.width),
        'height': parseInt(panelInfo.height),
        'min-width': parseInt(panelInfo['min-width']),
        'min-height': parseInt(panelInfo['min-height']),
        'max-width': parseInt(panelInfo['max-width']),
        'max-height': parseInt(panelInfo['max-height']),
    };

    // load layout-settings, and find windows by name
    var layoutProfile = Editor.loadProfile('layout.' + panelID, 'local' );
    if ( layoutProfile ) {
        windowOptions.x = parseInt(layoutProfile.x);
        windowOptions.y = parseInt(layoutProfile.y);
        windowOptions.width = parseInt(layoutProfile.width);
        windowOptions.height = parseInt(layoutProfile.height);
    }

    windowOptions['window-type'] = panelInfo.type || 'dockable';

    // NOTE: fixed-size window always use package.json settings
    if ( panelInfo.type === 'fixed-size' ) {
        windowOptions.width = parseInt(panelInfo.width);
        windowOptions.height = parseInt(panelInfo.height);
    }

    if ( isNaN(windowOptions.width) ) windowOptions.width = 400;
    if ( isNaN(windowOptions.height) ) windowOptions.height = 400;
    if ( isNaN(windowOptions['min-width']) ) windowOptions['min-width'] = 200;
    if ( isNaN(windowOptions['min-height']) ) windowOptions['min-height'] = 200;

    // create new window
    // DISABLE: currently, I don't want to support page
    // if ( panelInfo.page ) {
    //     url = panelInfo.page;
    // }

    //
    editorWin = new Editor.Window(windowName, windowOptions);
    _dock( panelID, editorWin );

    // BUG: https://github.com/atom/atom-shell/issues/1321
    editorWin.nativeWin.setContentSize( windowOptions.width, windowOptions.height );
    editorWin.nativeWin.setMenuBarVisibility(false);
    editorWin.load(Panel.templateUrl, {
        panelID: panelID
    });
    editorWin.focus();
};

Panel.close = function ( panelID ) {
    var editorWin = _undock(panelID);
    if ( editorWin ) {

        _saveLayout( editorWin, panelID );

        // check if we have other panels in the same window
        // if no panels left, we close the window
        var found = false;
        for ( var id in _panel2windows ) {
            if ( editorWin === _panel2windows[id] ) {
                found = true;
                break;
            }
        }

        // if not panel exists in this window, and it is not the main window, close it.
        if ( !found && !editorWin.isMainWindow ) {
            editorWin.close();
        }
    }
};

Panel.findWindow = function ( panelID ) {
    return _panel2windows[panelID];
};

Panel.findWindows = function (packageName) {
    var wins = [];

    for ( var p in _panel2windows ) {
        var pair = p.split('.');
        if ( pair.length !== 2 ) {
            continue;
        }

        var name = pair[1];
        if ( name === packageName ) {
            var editorWin = _panel2windows[p];
            if ( wins.indexOf (editorWin) === -1 )
                wins.push(editorWin);
        }
    }

    return wins;
};

Panel.findPanels = function ( packageName ) {
    var panelIDs = [];
    for ( var p in _panel2windows ) {
        var pair = p.split('.');
        if ( pair.length !== 2 ) {
            continue;
        }

        var name = pair[0];
        if ( name === packageName ) {
            panelIDs.push(pair);
        }
    }

    return panelIDs;
};

Panel.closeAll = function (packageName) {
    var panelIDs = Panel.findPanels(packageName);
    for (var i = 0; i < panelIDs.length; ++i) {
        Panel.close( panelIDs[i] );
    }
};

// NOTE: this only invoked in fire-window on-closed event
Panel._onWindowClosed = function ( editorWin ) {
    for ( var id in _panel2windows ) {
        var win = _panel2windows[id];
        if ( win === editorWin ) {
            _saveLayout( editorWin, id );
            delete _panel2windows[id];
        }
    }
};

// ========================================
// Ipc
// ========================================

Ipc.on('panel:query-info', function ( reply, panelID ) {
    if ( !panelID ) {
        Editor.error( 'Empty panelID' );
        reply();
        return;
    }

    // get panelInfo
    var panelInfo = Editor.Package.panelInfo(panelID);
    if ( panelInfo ) {
        // load profiles
        for ( var type in panelInfo.profiles ) {
            var profile = panelInfo.profiles[type];
            profile = Editor.loadProfile( panelID, type, profile );
            panelInfo.profiles[type] = profile;
        }
    }

    //
    reply(panelInfo);
});

Ipc.on('panel:ready', function ( panelID ) {
    var argv = _panel2argv[panelID];
    Editor.sendToPanel( panelID, 'panel:open', argv );
});

Ipc.on('panel:open', function ( panelID, argv ) {
    Panel.open( panelID, argv );
});

Ipc.on('panel:dock', function ( event, panelID ) {
    var browserWin = BrowserWindow.fromWebContents( event.sender );
    var editorWin = Editor.Window.find(browserWin);
    _dock( panelID, editorWin );
});

Ipc.on('panel:close', function ( panelID ) {
    Panel.close( panelID );
});

Ipc.on('panel:wait-for-close', function ( reply, panelID ) {
    Panel.close( panelID );
    reply();
});

//
Ipc.on('panel:save-profile', function ( panelID, type, panelProfile ) {
    var profile = Editor.loadProfile( panelID, type );
    if ( profile ) {
        profile.clear();
        Editor.JS.mixin(profile, panelProfile);
        profile.save();
    }
});

module.exports = Panel;
<|endoftext|>


<commit_before>var validator = require('validator');
var reqParams = require('./req_params');
var util = require('util');

var emptyBody = {
    code: 400,
    message: 'Request body cannot be empty!'
};

var invalidMongoParam = '%s is required and should have valid format';

//Project Rest Api validators
exports.validateSaveProject = function(req, res, next) {
    var project = req.body;
    if(project) {
        req.checkBody('name', 'Project name is required').notEmpty();
        if(!project._id) {
            req.checkBody('companyId', util.format(invalidMongoParam, 'Company id'))
                .notEmpty().isMongoId();
        }

        returnErrors(req, res, next);

    } else {
        res.status(emptyBody.code).json({msg: emptyBody.message});
    }
};

exports.validateGetProjectById = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetProjectByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//User Rest API validators
exports.validateGetUserByProjectId = function(req, res, next) {
    req.check(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetUserByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();
    returnErrors(req, res, next);
};

exports.validateReplaceAssignment = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    var user = req.body;
    if(!user) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }

    req.checkBody('_id', 'User id is required').notEmpty().isMongoId();
    req.checkBody('assignments', 'Incorrect Assignments (Check: userId, projectId, projectName)')
        .optional().isAssignments();

    returnErrors(req, res, next);
};

//Company Rest API validation
exports.validateUpdateCompany = function(req, res, next) {
    var company = req.body;
    if(!company) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    next();
};

exports.validateGetCompanyById = function(req, res, next) {
    req.checkParams(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//Timelog Rest API validation
exports.validateGetTimelogByDates = function(req, res, next) {
    req.checkParams(reqParams.userIdParam, util.format(invalidMongoParam, 'User id param'))
            .notEmpty().isMongoId();
    req.checkQuery(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();
    req.checkQuery(reqParams.startDateParam, util.format(invalidMongoParam, 'Start date param'))
            .notEmpty().isDate();
    req.checkQuery(reqParams.endDateParam, util.format(invalidMongoParam, 'End date param'))
            .notEmpty().isDate();

    returnErrors(req, res, next);
};

exports.validateDeleteTimelog = function(req, res, next) {
    req.checkParams(reqParams.userIdParam, util.format(invalidMongoParam, 'Timelog id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateSaveTimelog = function(req, res, next) {
    var timelogs = req.body;
    if(!timelogs) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    req.checkBody('timelog', 'Incorrect timelog (Check: date, userId, projectId, projectName)')
        .isTimelog();

    returnErrors(req, res, next);
};

//Custom validators for express-validator
exports.isArray = function(value) {
    return Array.isArray(value);
};

exports.timelogs = function(values) {
    if(Array.isArray(values)) {
        return values.every(function(val){
            var isValid = true;
            if(val._id){
                isValid = validator.isMongoId(val._id);
            }
            isValid = isValid
                && validator.isMongoId(val.userId) //required && format
                && validator.isMongoId(val.projectId) //required && format
                && validator.isLength(val.projectName, 1) //required
                && validator.isDate(val.date); //required && format
            return isValid;
        });
    }
    return false;
};

exports.assignments = function(values) {
    if(values.length) { // if array is empty
        return true;
    }

    if(Array.isArray(values)) {
        return values.every(function(val){
            return validator.isMongoId(val.projectId)
                && validator.isLength(val.projectName, 1)
                && validator.isMongoId(val.userId);
        });
    }
    return false;
};

//Private part
function returnErrors(req, res, next) {
    var errors = req.validationErrors(true);
    if(errors) {
        res.status(400).json(errors);
        return;
    }

    next();
}<commit_msg>Improve assignment validation<commit_after>var validator = require('validator');
var reqParams = require('./req_params');
var util = require('util');

var emptyBody = {
    code: 400,
    message: 'Request body cannot be empty!'
};

var invalidMongoParam = '%s is required and should have valid format';

//Project Rest Api validators
exports.validateSaveProject = function(req, res, next) {
    var project = req.body;
    if(project) {
        req.checkBody('name', 'Project name is required').notEmpty();
        if(!project._id) {
            req.checkBody('companyId', util.format(invalidMongoParam, 'Company id'))
                .notEmpty().isMongoId();
        }

        returnErrors(req, res, next);

    } else {
        res.status(emptyBody.code).json({msg: emptyBody.message});
    }
};

exports.validateGetProjectById = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetProjectByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//User Rest API validators
exports.validateGetUserByProjectId = function(req, res, next) {
    req.check(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateGetUserByCompanyId = function(req, res, next) {
    req.check(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();
    returnErrors(req, res, next);
};

exports.validateReplaceAssignment = function(req, res, next) {
    req.checkParams(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();

    var user = req.body;
    if(!user) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }

    req.checkBody('_id', util.format(invalidMongoParam, 'User id')).notEmpty().isMongoId();
    req.checkBody('assignments', 'Incorrect Assignments (Check: userId, projectId, projectName)')
        .optional().isAssignments(req.params[reqParams.projectIdParam], req.body._id);

    returnErrors(req, res, next);
};

//Company Rest API validation
exports.validateUpdateCompany = function(req, res, next) {
    var company = req.body;
    if(!company) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    next();
};

exports.validateGetCompanyById = function(req, res, next) {
    req.checkParams(reqParams.companyIdParam, util.format(invalidMongoParam, 'Company id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

//Timelog Rest API validation
exports.validateGetTimelogByDates = function(req, res, next) {
    req.checkParams(reqParams.userIdParam, util.format(invalidMongoParam, 'User id param'))
            .notEmpty().isMongoId();
    req.checkQuery(reqParams.projectIdParam, util.format(invalidMongoParam, 'Project id param'))
            .notEmpty().isMongoId();
    req.checkQuery(reqParams.startDateParam, util.format(invalidMongoParam, 'Start date param'))
            .notEmpty().isDate();
    req.checkQuery(reqParams.endDateParam, util.format(invalidMongoParam, 'End date param'))
            .notEmpty().isDate();

    returnErrors(req, res, next);
};

exports.validateDeleteTimelog = function(req, res, next) {
    req.checkParams(reqParams.userIdParam, util.format(invalidMongoParam, 'Timelog id param'))
            .notEmpty().isMongoId();

    returnErrors(req, res, next);
};

exports.validateSaveTimelog = function(req, res, next) {
    var timelogs = req.body;
    if(!timelogs) {
        res.status(emptyBody.code).json({msg: emptyBody.message});
        return;
    }
    req.checkBody('timelog', 'Incorrect timelog (Check: date, userId, projectId, projectName)')
        .isTimelog();

    returnErrors(req, res, next);
};

//Custom validators for express-validator
exports.isArray = function(value) {
    return Array.isArray(value);
};

exports.timelogs = function(values) {
    if(Array.isArray(values)) {
        return values.every(function(val){
            var isValid = true;
            if(val._id){
                isValid = validator.isMongoId(val._id);
            }
            isValid = isValid
                && validator.isMongoId(val.userId) //required && format
                && validator.isMongoId(val.projectId) //required && format
                && validator.isLength(val.projectName, 1) //required
                && validator.isDate(val.date); //required && format
            return isValid;
        });
    }
    return false;
};

exports.assignments = function(values, projectId, userId) {
    if(!values.length) { // if array is empty
        return true;
    }

    if(Array.isArray(values)) {
        return values.every(function(val){
            return validator.isMongoId(val.projectId)
                && validator.isMongoId(projectId)
                && (val.projectId.equals(projectId))
                && validator.isLength(val.projectName, 1)
                && validator.isMongoId(val.userId)
                && validator.isMongoId(userId)
                && (val.userId.equals(userId));
        });
    }
    return false;
};

//Private part
function returnErrors(req, res, next) {
    var errors = req.validationErrors(true);
    if(errors) {
        res.status(400).json(errors);
        return;
    }

    next();
}<|endoftext|>