<commit_before>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/field/field') ;
require('mixins/editable') ;

/**
  @class

  A text field is an input element with type "text".  This view adds support
  for hinted values, etc.

  @extends SC.FieldView
  @extends SC.Editable
  @author Charles Jolley
*/
SC.TextFieldView = SC.FieldView.extend(SC.Editable,
/** @scope SC.TextFieldView.prototype */ {

  emptyElement: '<input type="text" value="" />',

  // PROPERTIES

  /**
    The hint to display while the field is not active.  Can be a loc key.
  */
  hint: null,

  /**
    automatically set to YES if the hint is currently showing.
  */
  isHintShowing: false,

  /**
    If YES then the text field is currently editing.
  */
  isEditing: NO,

  // PRIVATE SUPPORT METHODS
  init: function() {

    // compatibility...
    if (this.hint == null) {
      this.hint = this.rootElement.getAttribute('hint') ;
    }

    if (this.validator == null) {
      this.validator = this.rootElement.getAttribute('validate') ;
    }

    arguments.callee.base.call(this) ;

    // observe important events for this field.
    var focusListener = this._fieldDidFocus.bindAsEventListener(this) ;
    Event.observe(this.rootElement, 'focus', focusListener) ;

    var blurListener = this._fieldDidBlur.bindAsEventListener(this) ;
    Event.observe(this.rootElement, 'blur', blurListener) ;

    this._updateFieldHint() ;
  },

  // FOCUS AND BLUR EVENTS -
  // These should be hooked into the firstResponder system..
  _fieldDidFocus: function() {
    if (!this._isFocused) {
      this._isFocused = true ;
      this.becomeFirstResponder() ;
    }
  },

  _fieldDidBlur: function() {
    if (this._isFocused) {
      this._isFocused = false ;
      this.resignFirstResponder() ;
    }
  },

  /**
    tied to the isEnabled state
  */
  acceptsFirstResponder: function() {
    return this.get('isEnabled');
  }.property('isEnabled'),

  // First Responder
  // When we become first responder, make sure the field gets focus and
  // the hint value is hidden if needed.

  // when we become first responder, focus the text field if needed and
  // hide the hint text.
  /** @private */
  didBecomeFirstResponder: function() {

    // focus the text field.
    if (!this._isFocused) {
      this._isFocused = true ;
      if (this.get('isVisibleInWindow')) {
        this.rootElement.focus();
        this.rootElement.select.bind(this.rootElement).delay(0.05);
      }
    }

    // hide the hint text if it is showing.
    this._updateFieldHint() ;
  },

  // when we lose first responder, blur the text field if needed and show
  // the hint text if needed.
  /** @private */
  willLoseFirstResponder: function() {

    if (this._isFocused) {
      this._isFocused = false ;
      return this.rootElement.blur() ;
    }

    this._value = this.rootElement.value ;
    this.fieldValueDidChange() ;
    this._updateFieldHint() ;
  },

  _isFocused: false,

  _updateFieldHint: function() {

    // show the hint if:
    // - flag is true
    // - hint != null or empty.
    // - this._value = null or empty.
    var hint = this.get('hint') ;
    var showHint = !!(!this._isFocused && ((this._value == null) || this._value == '') && (hint)) ;

    this.setClassName('show-hint', showHint);
    this.rootElement.value = (showHint) ? hint : (this._value || '') ;
    this.set('isHintShowing', showHint);
  },

  // field value updates...
  /** @private */
  getFieldValue: function() {
    return this._value ;
  },

  /** @private */
  setFieldValue: function(value) {
    if (this._value == value) return ;
    this._value = value ;
    this._updateFieldHint() ;
  },

  /** @private */
  mouseDown: function(e)
  {
    e._stopWhenHandled = false;
    return false;
  },

  // trap key-press events and notify as needed.
  /** @private */
  keyDown: function(evt) {
    if (this._value != this.rootElement.value) {
      this._value = this.rootElement.value ;
      this.fieldValueDidChange(true) ;
    }

    return this.interpretKeyEvents(evt); // start bubbling key events...
    //return false;
  },

  /** @private */
  keyUp: function() {
    if (this._value != this.rootElement.value) {
      this._value = this.rootElement.value ;
      this.fieldValueDidChange(true) ;
    }
  },

  // if make a text field first responder before the view becomes visible,
  // then focus the text field when it does become visible.
  _focusOnVisible: function() {
    if (this.get('isVisibleInWindow') && this._isFocused) {
      this.rootElement.focus() ;
      this.rootElement.select.bind(this.rootElement).delay(0.05);
    }
  }.observes('isVisibleInWindow'),

  // THESE ARE DUMMY IMPLEMENTATIONS OF THE REPONDER METHODS FOR KEYBOARD
  // ACTIONS HANDLED BY THE BROWSER.  This avoids having the responder
  // bubble up these items.

  /** @private */
  deleteBackward: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  deleteForward: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveLeft: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveRight: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveUp: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveDown: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveLeftAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveRightAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveUpAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveDownAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveToBeginningOfDocument: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveToEndOfDocument: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  selectAll: function(evt) { evt._stopWhenHandled = false; return true; }

}) ;
<commit_msg>Fixed IE Issue:  Replaced code that was attempting to modify a DOM function  ( select )<commit_after>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/field/field') ;
require('mixins/editable') ;

/**
  @class

  A text field is an input element with type "text".  This view adds support
  for hinted values, etc.

  @extends SC.FieldView
  @extends SC.Editable
  @author Charles Jolley
*/
SC.TextFieldView = SC.FieldView.extend(SC.Editable,
/** @scope SC.TextFieldView.prototype */ {

  emptyElement: '<input type="text" value="" />',

  // PROPERTIES

  /**
    The hint to display while the field is not active.  Can be a loc key.
  */
  hint: null,

  /**
    automatically set to YES if the hint is currently showing.
  */
  isHintShowing: false,

  /**
    If YES then the text field is currently editing.
  */
  isEditing: NO,

  // PRIVATE SUPPORT METHODS
  init: function() {

    // compatibility...
    if (this.hint == null) {
      this.hint = this.rootElement.getAttribute('hint') ;
    }

    if (this.validator == null) {
      this.validator = this.rootElement.getAttribute('validate') ;
    }

    arguments.callee.base.call(this) ;

    // observe important events for this field.
    var focusListener = this._fieldDidFocus.bindAsEventListener(this) ;
    Event.observe(this.rootElement, 'focus', focusListener) ;

    var blurListener = this._fieldDidBlur.bindAsEventListener(this) ;
    Event.observe(this.rootElement, 'blur', blurListener) ;

    this._updateFieldHint() ;
  },

  // FOCUS AND BLUR EVENTS -
  // These should be hooked into the firstResponder system..
  _fieldDidFocus: function() {
    if (!this._isFocused) {
      this._isFocused = true ;
      this.becomeFirstResponder() ;
    }
  },

  _fieldDidBlur: function() {
    if (this._isFocused) {
      this._isFocused = false ;
      this.resignFirstResponder() ;
    }
  },

  /**
    tied to the isEnabled state
  */
  acceptsFirstResponder: function() {
    return this.get('isEnabled');
  }.property('isEnabled'),

  // First Responder
  // When we become first responder, make sure the field gets focus and
  // the hint value is hidden if needed.

  // when we become first responder, focus the text field if needed and
  // hide the hint text.
  /** @private */
  didBecomeFirstResponder: function() {

    // focus the text field.
    if (!this._isFocused) {
      this._isFocused = true ;
      if (this.get('isVisibleInWindow')) {
        this.rootElement.focus();

		this.invokeLater(this._selectRootElement, 1) ;

      }
    }

    // hide the hint text if it is showing.
    this._updateFieldHint() ;
  },

  // In IE, you can't modify functions on DOM elements so we need to wrap the call to select()
  // like this.
  _selectRootElement: function() {
	this.rootElement.select() ;
  },

  // when we lose first responder, blur the text field if needed and show
  // the hint text if needed.
  /** @private */
  willLoseFirstResponder: function() {

    if (this._isFocused) {
      this._isFocused = false ;
      return this.rootElement.blur() ;
    }

    this._value = this.rootElement.value ;
    this.fieldValueDidChange() ;
    this._updateFieldHint() ;
  },

  _isFocused: false,

  _updateFieldHint: function() {

    // show the hint if:
    // - flag is true
    // - hint != null or empty.
    // - this._value = null or empty.
    var hint = this.get('hint') ;
    var showHint = !!(!this._isFocused && ((this._value == null) || this._value == '') && (hint)) ;

    this.setClassName('show-hint', showHint);
    this.rootElement.value = (showHint) ? hint : (this._value || '') ;
    this.set('isHintShowing', showHint);
  },

  // field value updates...
  /** @private */
  getFieldValue: function() {
    return this._value ;
  },

  /** @private */
  setFieldValue: function(value) {
    if (this._value == value) return ;
    this._value = value ;
    this._updateFieldHint() ;
  },

  /** @private */
  mouseDown: function(e)
  {
    e._stopWhenHandled = false;
    return false;
  },

  // trap key-press events and notify as needed.
  /** @private */
  keyDown: function(evt) {
    if (this._value != this.rootElement.value) {
      this._value = this.rootElement.value ;
      this.fieldValueDidChange(true) ;
    }

    return this.interpretKeyEvents(evt); // start bubbling key events...
    //return false;
  },

  /** @private */
  keyUp: function() {
    if (this._value != this.rootElement.value) {
      this._value = this.rootElement.value ;
      this.fieldValueDidChange(true) ;
    }
  },

  // if make a text field first responder before the view becomes visible,
  // then focus the text field when it does become visible.
  _focusOnVisible: function() {
    if (this.get('isVisibleInWindow') && this._isFocused) {
      this.rootElement.focus() ;
      this.rootElement.select.bind(this.rootElement).delay(0.05);
    }
  }.observes('isVisibleInWindow'),

  // THESE ARE DUMMY IMPLEMENTATIONS OF THE REPONDER METHODS FOR KEYBOARD
  // ACTIONS HANDLED BY THE BROWSER.  This avoids having the responder
  // bubble up these items.

  /** @private */
  deleteBackward: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  deleteForward: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveLeft: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveRight: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveUp: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveDown: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveLeftAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveRightAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveUpAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveDownAndModifySelection: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveToBeginningOfDocument: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  moveToEndOfDocument: function(evt) { evt._stopWhenHandled = false; return true; },

  /** @private */
  selectAll: function(evt) { evt._stopWhenHandled = false; return true; }

}) ;
<|endoftext|>


<commit_before>/*!
 * miner.js - inefficient miner for bcoin (because we can)
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var util = require('../utils/util');
var co = require('../utils/co');
var assert = require('assert');
var constants = require('../protocol/constants');
var AsyncObject = require('../utils/async');
var Address = require('../primitives/address');
var MinerBlock = require('./minerblock');
var BlockEntry = MinerBlock.BlockEntry;

/**
 * A bitcoin miner (supports mining witness blocks).
 * @exports Miner
 * @constructor
 * @param {Object} options
 * @param {Base58Address} options.address - Payout address.
 * @param {String?} [options.coinbaseFlags="mined by bcoin"]
 * @property {Boolean} running
 * @property {MinerBlock} attempt
 * @emits Miner#block
 * @emits Miner#status
 */

function Miner(options) {
  if (!(this instanceof Miner))
    return new Miner(options);

  AsyncObject.call(this);

  assert(options, 'Miner requires options.');
  assert(options.chain, 'Miner requires a blockchain.');

  this.chain = options.chain;
  this.mempool = options.mempool;
  this.network = this.chain.network;
  this.logger = options.logger || this.chain.logger;

  this.running = false;
  this.stopping = false;
  this.attempt = null;
  this.since = 0;

  this.version = -1;
  this.addresses = [];
  this.coinbaseFlags = new Buffer('mined by bcoin', 'ascii');

  this.minWeight = 0;
  this.maxWeight = 750000 * constants.WITNESS_SCALE_FACTOR;
  this.maxSigops = constants.block.MAX_SIGOPS_COST;
  this.priorityWeight = 50000 * constants.WITNESS_SCALE_FACTOR;
  this.minPriority = constants.tx.FREE_THRESHOLD;

  this._initOptions(options);
  this._init();
}

util.inherits(Miner, AsyncObject);

/**
 * Initialize the miner options.
 * @private
 */

Miner.prototype._initOptions = function _initOptions(options) {
  var i, flags;

  if (options.version != null) {
    assert(util.isNumber(options.version));
    this.version = options.version;
  }

  if (options.address)
    this.addAddress(options.address);

  if (options.addresses) {
    assert(Array.isArray(options.addresses));
    for (i = 0; i < options.addresses.length; i++)
      this.addAddress(options.addresses[i]);
  }

  if (options.coinbaseFlags) {
    flags = options.coinbaseFlags;
    if (typeof flags === 'string')
      flags = new Buffer(flags, 'utf8');
    assert(Buffer.isBuffer(flags));
    this.coinbaseFlags = flags;
  }

  if (options.minWeight != null) {
    assert(util.isNumber(options.minWeight));
    this.minWeight = options.minWeight;
  }

  if (options.maxWeight != null) {
    assert(util.isNumber(options.maxWeight));
    this.maxWeight = options.maxWeight;
  }

  if (options.maxSigops != null) {
    assert(util.isNumber(options.maxSigops));
    assert(options.maxSigops <= constants.block.MAX_SIGOPS_COST);
    this.maxSigops = options.maxSigops;
  }

  if (options.priorityWeight != null) {
    assert(util.isNumber(options.priorityWeight));
    this.priorityWeight = options.priorityWeight;
  }

  if (options.minPriority != null) {
    assert(util.isNumber(options.minPriority));
    this.minPriority = options.minPriority;
  }
};

/**
 * Initialize the miner.
 * @private
 */

Miner.prototype._init = function _init() {
  var self = this;

  this.chain.on('tip', function(tip) {
    if (!self.attempt)
      return;

    if (self.attempt.block.prevBlock === tip.prevBlock)
      self.attempt.destroy();
  });

  this.on('block', function(block, entry) {
    // Emit the block hex as a failsafe (in case we can't send it)
    self.logger.info('Found block: %d (%s).', entry.height, entry.rhash());
    self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
  });

  this.on('status', function(stat) {
    self.logger.info(
      'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
      stat.hashrate / 1000 | 0,
      stat.hashes,
      stat.target,
      stat.height,
      stat.best);
  });
};

/**
 * Open the miner, wait for the chain and mempool to load.
 * @alias Miner#open
 * @returns {Promise}
 */

Miner.prototype._open = co(function* open() {
  yield this.chain.open();

  if (this.mempool)
    yield this.mempool.open();

  this.logger.info('Miner loaded (flags=%s).',
    this.coinbaseFlags.toString('utf8'));
});

/**
 * Close the miner.
 * @alias Miner#close
 * @returns {Promise}
 */

Miner.prototype._close = co(function* close() {
  if (!this.running)
    return;

  if (this.stopping) {
    yield this._onStop();
    return;
  }

  yield this.stop();
});

/**
 * Start mining.
 * @param {Number?} version - Custom block version.
 */

Miner.prototype.start = co(function* start() {
  var self = this;
  var block, entry;

  assert(!this.running, 'Miner is already running.');

  this.running = true;
  this.stopping = false;

  for (;;) {
    this.attempt = null;

    try {
      this.attempt = yield this.createBlock();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.attempt.on('status', function(status) {
      self.emit('status', status);
    });

    try {
      block = yield this.attempt.mineAsync();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    if (!block)
      continue;

    try {
      entry = yield this.chain.add(block);
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.emit('block', block, entry);
  }

  this.emit('done');
});

/**
 * Stop mining.
 */

Miner.prototype.stop = co(function* stop() {
  assert(this.running, 'Miner is not running.');
  assert(!this.stopping, 'Miner is already stopping.');

  this.stopping = true;

  if (this.attempt)
    this.attempt.destroy();

  yield this._onDone();

  this.running = false;
  this.stopping = false;
  this.attempt = null;

  this.emit('stop');
});

/**
 * Wait for `done` event.
 */

Miner.prototype._onDone = function _onDone() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('done', resolve);
  });
};

/**
 * Wait for `stop` event.
 */

Miner.prototype._onStop = function _onStop() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('stop', resolve);
  });
};

/**
 * Create a block "attempt".
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns {@link MinerBlock}.
 */

Miner.prototype.createBlock = co(function* createBlock(tip, address) {
  var version = this.version;
  var now = this.network.now();
  var ts, locktime, median, target, attempt;

  if (!tip)
    tip = this.chain.tip;

  assert(tip);

  ts = Math.max(now, tip.ts + 1);
  locktime = ts;

  if (version === -1)
    version = yield this.chain.computeBlockVersion(tip);

  // Cheat on testnet.
  // if (this.network.pow.difficultyReset) {
  //   median = yield tip.getMedianTimeAsync();
  //   ts = cheat(tip.ts, median, now, this.network.pow.targetSpacing);
  //   locktime = ts;
  // }

  target = yield this.chain.getTargetAsync(ts, tip);

  if (this.chain.state.hasMTP())
    locktime = yield tip.getMedianTimeAsync();

  if (!address)
    address = this.getAddress();

  attempt = new MinerBlock({
    tip: tip,
    version: version,
    bits: target,
    locktime: locktime,
    flags: this.chain.state.flags,
    address: address,
    coinbaseFlags: this.coinbaseFlags,
    witness: this.chain.state.hasWitness(),
    network: this.network
  });

  this.build(attempt);

  return attempt;
});

/**
 * Mine a single block.
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns [{@link Block}].
 */

Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
  var attempt = yield this.createBlock(tip, address);
  return yield attempt.mineAsync();
});

/**
 * Notify the miner that a new tx has entered the mempool.
 * @param {MempoolEntry} entry
 */

Miner.prototype.notifyEntry = function notifyEntry() {
  if (!this.running)
    return;

  if (!this.attempt)
    return;

  if (++this.since > 20) {
    this.since = 0;
    this.attempt.destroy();
  }
};

/**
 * Add an address to the address list.
 * @param {Address} address
 */

Miner.prototype.addAddress = function addAddress(address) {
  this.addresses.push(Address(address));
};

/**
 * Get a random address from the address list.
 * @returns {Address}
 */

Miner.prototype.getAddress = function getAddress() {
  assert(this.addresses.length !== 0, 'No address passed in for miner.');
  return this.addresses[Math.random() * this.addresses.length | 0];
};

/**
 * Get mempool entries, sort by dependency order.
 * Prioritize by priority and fee rates.
 * @returns {MempoolEntry[]}
 */

Miner.prototype.build = function build(attempt) {
  var depMap = {};
  var block = attempt.block;
  var queue = new Queue(cmpPriority);
  var priority = true;
  var i, j, entry, item, tx, hash, input;
  var prev, deps, hashes, weight, sigops;

  if (!this.mempool)
    return [];

  hashes = this.mempool.getSnapshot();

  for (i = 0; i < hashes.length; i++) {
    hash = hashes[i];
    entry = this.mempool.getEntry(hash);
    item = BlockEntry.fromEntry(entry, attempt);
    tx = item.tx;

    if (tx.isCoinbase())
      throw new Error('Cannot add coinbase to block.');

    if (!tx.isFinal(attempt.height, attempt.locktime))
      continue;

    for (j = 0; j < tx.inputs.length; j++) {
      input = tx.inputs[j];
      prev = input.prevout.hash;

      if (!this.mempool.hasTX(prev))
        continue;

      item.depCount += 1;

      if (!depMap[prev])
        depMap[prev] = [];

      depMap[prev].push(item);
    }

    if (item.depCount > 0)
      continue;

    queue.push(item);
  }

  while (queue.size() > 0) {
    item = queue.pop();
    tx = item.tx;
    hash = item.hash;
    weight = attempt.weight;
    sigops = attempt.sigops;

    if (!attempt.witness && tx.hasWitness())
      continue;

    weight += tx.getWeight();

    if (weight > this.maxWeight)
      continue;

    sigops += item.sigops;

    // If we had a view we could do:
    // sigops += tx.getSigopsCost(view, attempt.flags);

    if (sigops > this.maxSigops)
      continue;

    if (priority) {
      if (weight > this.priorityWeight || item.priority < this.minPriority) {
        // Todo: Compare descendant rate with
        // cumulative fees and cumulative vsize.
        queue.cmp = cmpRate;
        priority = false;
        queue.push(item);
        continue;
      }
    } else {
      if (item.free && weight >= this.minWeight)
        continue;
    }

    attempt.weight = weight;
    attempt.sigops = sigops;
    attempt.fees += item.fee;
    attempt.items.push(item);

    block.txs.push(tx);

    deps = depMap[hash];

    if (!deps)
      continue;

    for (j = 0; j < deps.length; j++) {
      item = deps[j];
      if (--item.depCount === 0)
        queue.push(item);
    }
  }

  attempt.updateCoinbase();
  attempt.updateMerkle();

  assert(block.getWeight() <= attempt.weight);
};

/**
 * Queue
 * @constructor
 */

function Queue(cmp) {
  this.cmp = cmp;
  this.items = [];
}

Queue.prototype.size = function size() {
  return this.items.length;
};

Queue.prototype.push = function push(item) {
  util.binaryInsert(this.items, item, this.cmp);
};

Queue.prototype.pop = function pop() {
  return this.items.pop();
};

/*
 * Helpers
 */

function cmpPriority(a, b) {
  return a.priority - b.priority;
}

function cmpRate(a, b) {
  return a.rate - b.rate;
}

function cheat(prevTime, medianTime, now, targetSpacing) {
  var resetTime = prevTime + (targetSpacing * 3);

  // Already easy.
  if (now >= resetTime)
    return now;

  // time-too-new - 30 min
  if (resetTime > now + (1.5 * 60 * 60))
    return medianTime + 1;

  // time-too-old
  if (resetTime <= medianTime)
    return medianTime + 1;

  return resetTime;
}

/*
 * Expose
 */

module.exports = Miner;
<commit_msg>miner: failsafe for segwit.<commit_after>/*!
 * miner.js - inefficient miner for bcoin (because we can)
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var util = require('../utils/util');
var co = require('../utils/co');
var assert = require('assert');
var constants = require('../protocol/constants');
var AsyncObject = require('../utils/async');
var Address = require('../primitives/address');
var MinerBlock = require('./minerblock');
var BlockEntry = MinerBlock.BlockEntry;

/**
 * A bitcoin miner (supports mining witness blocks).
 * @exports Miner
 * @constructor
 * @param {Object} options
 * @param {Base58Address} options.address - Payout address.
 * @param {String?} [options.coinbaseFlags="mined by bcoin"]
 * @property {Boolean} running
 * @property {MinerBlock} attempt
 * @emits Miner#block
 * @emits Miner#status
 */

function Miner(options) {
  if (!(this instanceof Miner))
    return new Miner(options);

  AsyncObject.call(this);

  assert(options, 'Miner requires options.');
  assert(options.chain, 'Miner requires a blockchain.');

  this.chain = options.chain;
  this.mempool = options.mempool;
  this.network = this.chain.network;
  this.logger = options.logger || this.chain.logger;

  this.running = false;
  this.stopping = false;
  this.attempt = null;
  this.since = 0;

  this.version = -1;
  this.addresses = [];
  this.coinbaseFlags = new Buffer('mined by bcoin', 'ascii');

  this.minWeight = 0;
  this.maxWeight = 750000 * constants.WITNESS_SCALE_FACTOR;
  this.maxSigops = constants.block.MAX_SIGOPS_COST;
  this.priorityWeight = 50000 * constants.WITNESS_SCALE_FACTOR;
  this.minPriority = constants.tx.FREE_THRESHOLD;

  this._initOptions(options);
  this._init();
}

util.inherits(Miner, AsyncObject);

/**
 * Initialize the miner options.
 * @private
 */

Miner.prototype._initOptions = function _initOptions(options) {
  var i, flags;

  if (options.version != null) {
    assert(util.isNumber(options.version));
    this.version = options.version;
  }

  if (options.address)
    this.addAddress(options.address);

  if (options.addresses) {
    assert(Array.isArray(options.addresses));
    for (i = 0; i < options.addresses.length; i++)
      this.addAddress(options.addresses[i]);
  }

  if (options.coinbaseFlags) {
    flags = options.coinbaseFlags;
    if (typeof flags === 'string')
      flags = new Buffer(flags, 'utf8');
    assert(Buffer.isBuffer(flags));
    this.coinbaseFlags = flags;
  }

  if (options.minWeight != null) {
    assert(util.isNumber(options.minWeight));
    this.minWeight = options.minWeight;
  }

  if (options.maxWeight != null) {
    assert(util.isNumber(options.maxWeight));
    this.maxWeight = options.maxWeight;
  }

  if (options.maxSigops != null) {
    assert(util.isNumber(options.maxSigops));
    assert(options.maxSigops <= constants.block.MAX_SIGOPS_COST);
    this.maxSigops = options.maxSigops;
  }

  if (options.priorityWeight != null) {
    assert(util.isNumber(options.priorityWeight));
    this.priorityWeight = options.priorityWeight;
  }

  if (options.minPriority != null) {
    assert(util.isNumber(options.minPriority));
    this.minPriority = options.minPriority;
  }
};

/**
 * Initialize the miner.
 * @private
 */

Miner.prototype._init = function _init() {
  var self = this;

  this.chain.on('tip', function(tip) {
    if (!self.attempt)
      return;

    if (self.attempt.block.prevBlock === tip.prevBlock)
      self.attempt.destroy();
  });

  this.on('block', function(block, entry) {
    // Emit the block hex as a failsafe (in case we can't send it)
    self.logger.info('Found block: %d (%s).', entry.height, entry.rhash());
    self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
  });

  this.on('status', function(stat) {
    self.logger.info(
      'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
      stat.hashrate / 1000 | 0,
      stat.hashes,
      stat.target,
      stat.height,
      stat.best);
  });
};

/**
 * Open the miner, wait for the chain and mempool to load.
 * @alias Miner#open
 * @returns {Promise}
 */

Miner.prototype._open = co(function* open() {
  yield this.chain.open();

  if (this.mempool)
    yield this.mempool.open();

  this.logger.info('Miner loaded (flags=%s).',
    this.coinbaseFlags.toString('utf8'));
});

/**
 * Close the miner.
 * @alias Miner#close
 * @returns {Promise}
 */

Miner.prototype._close = co(function* close() {
  if (!this.running)
    return;

  if (this.stopping) {
    yield this._onStop();
    return;
  }

  yield this.stop();
});

/**
 * Start mining.
 * @param {Number?} version - Custom block version.
 */

Miner.prototype.start = co(function* start() {
  var self = this;
  var block, entry;

  assert(!this.running, 'Miner is already running.');

  this.running = true;
  this.stopping = false;

  for (;;) {
    this.attempt = null;

    try {
      this.attempt = yield this.createBlock();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.attempt.on('status', function(status) {
      self.emit('status', status);
    });

    try {
      block = yield this.attempt.mineAsync();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    if (!block)
      continue;

    try {
      entry = yield this.chain.add(block);
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.emit('block', block, entry);
  }

  this.emit('done');
});

/**
 * Stop mining.
 */

Miner.prototype.stop = co(function* stop() {
  assert(this.running, 'Miner is not running.');
  assert(!this.stopping, 'Miner is already stopping.');

  this.stopping = true;

  if (this.attempt)
    this.attempt.destroy();

  yield this._onDone();

  this.running = false;
  this.stopping = false;
  this.attempt = null;

  this.emit('stop');
});

/**
 * Wait for `done` event.
 */

Miner.prototype._onDone = function _onDone() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('done', resolve);
  });
};

/**
 * Wait for `stop` event.
 */

Miner.prototype._onStop = function _onStop() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('stop', resolve);
  });
};

/**
 * Create a block "attempt".
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns {@link MinerBlock}.
 */

Miner.prototype.createBlock = co(function* createBlock(tip, address) {
  var version = this.version;
  var now = this.network.now();
  var ts, locktime, median, target, attempt;

  if (!tip)
    tip = this.chain.tip;

  assert(tip);

  ts = Math.max(now, tip.ts + 1);
  locktime = ts;

  if (version === -1)
    version = yield this.chain.computeBlockVersion(tip);

  // Cheat on testnet.
  // if (this.network.pow.difficultyReset) {
  //   median = yield tip.getMedianTimeAsync();
  //   ts = cheat(tip.ts, median, now, this.network.pow.targetSpacing);
  //   locktime = ts;
  // }

  target = yield this.chain.getTargetAsync(ts, tip);

  if (this.chain.state.hasMTP())
    locktime = yield tip.getMedianTimeAsync();

  if (!address)
    address = this.getAddress();

  attempt = new MinerBlock({
    tip: tip,
    version: version,
    bits: target,
    locktime: locktime,
    flags: this.chain.state.flags,
    address: address,
    coinbaseFlags: this.coinbaseFlags,
    witness: this.chain.state.hasWitness(),
    network: this.network
  });

  this.build(attempt);

  return attempt;
});

/**
 * Mine a single block.
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns [{@link Block}].
 */

Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
  var attempt = yield this.createBlock(tip, address);
  return yield attempt.mineAsync();
});

/**
 * Notify the miner that a new tx has entered the mempool.
 * @param {MempoolEntry} entry
 */

Miner.prototype.notifyEntry = function notifyEntry() {
  if (!this.running)
    return;

  if (!this.attempt)
    return;

  if (++this.since > 20) {
    this.since = 0;
    this.attempt.destroy();
  }
};

/**
 * Add an address to the address list.
 * @param {Address} address
 */

Miner.prototype.addAddress = function addAddress(address) {
  this.addresses.push(Address(address));
};

/**
 * Get a random address from the address list.
 * @returns {Address}
 */

Miner.prototype.getAddress = function getAddress() {
  assert(this.addresses.length !== 0, 'No address passed in for miner.');
  return this.addresses[Math.random() * this.addresses.length | 0];
};

/**
 * Get mempool entries, sort by dependency order.
 * Prioritize by priority and fee rates.
 * @returns {MempoolEntry[]}
 */

Miner.prototype.build = function build(attempt) {
  var depMap = {};
  var block = attempt.block;
  var queue = new Queue(cmpPriority);
  var priority = true;
  var i, j, entry, item, tx, hash, input;
  var prev, deps, hashes, weight, sigops;

  if (!this.mempool)
    return [];

  hashes = this.mempool.getSnapshot();

  for (i = 0; i < hashes.length; i++) {
    hash = hashes[i];
    entry = this.mempool.getEntry(hash);
    item = BlockEntry.fromEntry(entry, attempt);
    tx = item.tx;

    if (tx.isCoinbase())
      throw new Error('Cannot add coinbase to block.');

    if (!tx.isFinal(attempt.height, attempt.locktime))
      continue;

    if (!attempt.witness && tx.hasWitness())
      continue;

    for (j = 0; j < tx.inputs.length; j++) {
      input = tx.inputs[j];
      prev = input.prevout.hash;

      if (!this.mempool.hasTX(prev))
        continue;

      item.depCount += 1;

      if (!depMap[prev])
        depMap[prev] = [];

      depMap[prev].push(item);
    }

    if (item.depCount > 0)
      continue;

    queue.push(item);
  }

  while (queue.size() > 0) {
    item = queue.pop();
    tx = item.tx;
    hash = item.hash;
    weight = attempt.weight;
    sigops = attempt.sigops;

    if (!attempt.witness && tx.hasWitness())
      continue;

    weight += tx.getWeight();

    if (weight > this.maxWeight)
      continue;

    sigops += item.sigops;

    // If we had a view we could do:
    // sigops += tx.getSigopsCost(view, attempt.flags);

    if (sigops > this.maxSigops)
      continue;

    if (priority) {
      if (weight > this.priorityWeight || item.priority < this.minPriority) {
        // Todo: Compare descendant rate with
        // cumulative fees and cumulative vsize.
        queue.cmp = cmpRate;
        priority = false;
        queue.push(item);
        continue;
      }
    } else {
      if (item.free && weight >= this.minWeight)
        continue;
    }

    attempt.weight = weight;
    attempt.sigops = sigops;
    attempt.fees += item.fee;
    attempt.items.push(item);

    block.txs.push(tx);

    deps = depMap[hash];

    if (!deps)
      continue;

    for (j = 0; j < deps.length; j++) {
      item = deps[j];
      if (--item.depCount === 0)
        queue.push(item);
    }
  }

  attempt.updateCoinbase();
  attempt.updateMerkle();

  assert(block.getWeight() <= attempt.weight);
};

/**
 * Queue
 * @constructor
 */

function Queue(cmp) {
  this.cmp = cmp;
  this.items = [];
}

Queue.prototype.size = function size() {
  return this.items.length;
};

Queue.prototype.push = function push(item) {
  util.binaryInsert(this.items, item, this.cmp);
};

Queue.prototype.pop = function pop() {
  return this.items.pop();
};

/*
 * Helpers
 */

function cmpPriority(a, b) {
  return a.priority - b.priority;
}

function cmpRate(a, b) {
  return a.rate - b.rate;
}

function cheat(prevTime, medianTime, now, targetSpacing) {
  var resetTime = prevTime + (targetSpacing * 3);

  // Already easy.
  if (now >= resetTime)
    return now;

  // time-too-new - 30 min
  if (resetTime > now + (1.5 * 60 * 60))
    return medianTime + 1;

  // time-too-old
  if (resetTime <= medianTime)
    return medianTime + 1;

  return resetTime;
}

/*
 * Expose
 */

module.exports = Miner;
<|endoftext|>


<commit_before>const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const async = require('async');
const _ = require('underscore');
const bitcore = require('bitcore-lib');

const config = require('../config');
const Transaction = mongoose.model('Transaction');
const Coin = mongoose.model('Coin');

const BlockSchema = new Schema({
  network: String,
  height: Number,
  hash: String,
  version: Number,
  merkleRoot: String,
  time: Date,
  timeNormalized: Date,
  nonce: Number,
  previousBlockHash: String,
  nextBlockHash: String,
  transactionCount: Number,
  size: Number,
  bits: Number,
  reward: Number,
  processed: Boolean
});

BlockSchema.index({hash: 1});
BlockSchema.index({height: 1});
BlockSchema.index({timeNormalized: 1});
BlockSchema.index({previousBlockHash: 1});

BlockSchema.statics.addBlock = function(block, callback){
  let header = block.header.toObject();
  let blockTime = header.time * 1000;
  let blockTimeNormalized;
  let height;
  async.series([
    function(cb){
      Block.handleReorg(header, cb);
    },
    function(cb){
        Block.findOne({ hash: header.prevHash}, function(err, previousBlock) {
        if(err) {
          return cb(err);
        }
        blockTimeNormalized = blockTime;
        if(previousBlock && blockTime <= previousBlock.timeNormalized.getTime()) {
          blockTimeNormalized = previousBlock.timeNormalized.getTime() + 1;
        }
        height = (previousBlock && previousBlock.height + 1) || 1;
        Block.update({hash: header.hash}, {
          network: config.network,
          height: height,
          version: header.version,
          previousBlockHash: header.prevHash,
          merkleRoot: header.merkleRoot,
          time: new Date(blockTime),
          timeNormalized: new Date(blockTimeNormalized),
          bits: header.bits,
          nonce: header.nonce,
          transactionCount: block.transactions.length,
          size: block.toBuffer().length,
          reward: block.transactions[0].outputAmount
        }, {upsert: true}, function(err){
          if (err){
            return cb(err);
          }
          if (!previousBlock){
            return cb();
          }
          previousBlock.nextBlockHash = header.hash;
          previousBlock.save(cb);
        });
      });
    },
    function (cb) {
      async.eachLimit(block.transactions, config.maxPoolSize, function (tx, txCb) {
        Transaction.mintCoins({
          transaction: tx,
          network: config.network,
          mintHeight: height
        }, txCb);
      }, cb);
    },
    function (cb) {
      async.eachLimit(block.transactions, config.maxPoolSize, function (tx, txCb) {
        Transaction.spendCoins({
          transaction: tx,
          spentHeight: height
        }, txCb);
      }, cb);
    },
    function (cb) {
      async.eachLimit(block.transactions, config.maxPoolSize, function (tx, txCb) {
        Transaction.addTransaction({
          transaction: tx,
          network: config.network,
          blockHash: header.hash,
          blockHeight: height,
          blockTime: blockTime,
          blockTimeNormalized: blockTimeNormalized
        }, txCb);
      }, cb);
    }
  ], function(err){
    if(err){
      return callback(err);
    }
    Block.update({hash: header.hash}, {$set: {processed:true}}, callback);
  });
};

BlockSchema.statics.getPoolInfo = function(coinbase){
  //TODO need to make this actually parse the coinbase input and map to miner strings
  // also should go somewhere else
  return 'miningPool';
};

BlockSchema.statics.getLocalTip = function(callback) {
  Block.find({processed: true, network: config.network}).sort({height: -1}).limit(1).exec(function(err, bestBlock) {
    if(err) {
      return callback(err);
    }
    bestBlock = bestBlock[0] || {height: 0};
    callback(null, bestBlock);
  });
};

BlockSchema.statics.getLocatorHashes = function(callback) {
  Block.find({processed: true, network: config.network}).sort({height: -1}).limit(30).exec(function(err, locatorBlocks) {
    if(err) {
      return callback(err);
    }
    if(locatorBlocks.length < 2) {
      return callback(null, [Array(65).join('0')]);
    }
    locatorBlocks = _.pluck(locatorBlocks, 'hash');
    callback(null, locatorBlocks);
  });
};

BlockSchema.statics.handleReorg = function(header, callback) {
  Block.getLocalTip(function(err, localTip) {
    if(err) {
      return callback(err);
    }
    if (header && localTip.hash === header.prevHash) {
      return callback();
    }
    if(localTip.height === 0){
      return callback();
    }
    console.log(`Resetting tip to ${localTip.previousBlockHash}`);
    async.series([
      function(cb){
        Block.remove({ network: config.network, height: { $gte: localTip.height } }, cb);
      },
      function(cb){
        Transaction.remove({ network: config.network, blockHeight: { $gte: localTip.height } }, cb);
      },
      function(cb){
        Coin.remove({ network: config.network, mintHeight: { $gte: localTip.height } }, cb);
      },
      function (cb) {
        Coin.update({ network: config.network, spentHeight: { $gte: localTip.height } }, {
          $set: {spentTxid: null, spentHeight: -1}
        }, { multi: true }, cb);
      }
    ], callback);
  });
};

BlockSchema.statics._apiTransform = function(block, options){
  let transform = {
    hash: block.hash,
    height: block.height,
    version: block.version,
    size: block.size,
    merkleRoot: block.merkleRoot,
    time: block.time,
    timeNormalized: block.timeNormalized,
    nonce: block.nonce,
    bits: block.bits,
    difficulty: block.difficulty,
    chainWork: block.chainWork,
    previousBlockHash: block.previousBlockHash,
    nextBlockHash: block.nextBlockHash,
    reward: block.reward,
    isMainChain: block.mainChain,
    minedBy: Block.getPoolInfo(block.minedBy)
  };
  if(options && options.object){
    return transform;
  }
  return JSON.stringify(transform);
};

var Block = module.exports = mongoose.model('Block', BlockSchema);<commit_msg>cleanup<commit_after>const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const async = require('async');
const _ = require('underscore');

const config = require('../config');
const Transaction = mongoose.model('Transaction');
const Coin = mongoose.model('Coin');

const BlockSchema = new Schema({
  network: String,
  height: Number,
  hash: String,
  version: Number,
  merkleRoot: String,
  time: Date,
  timeNormalized: Date,
  nonce: Number,
  previousBlockHash: String,
  nextBlockHash: String,
  transactionCount: Number,
  size: Number,
  bits: Number,
  reward: Number,
  processed: Boolean
});

BlockSchema.index({hash: 1});
BlockSchema.index({height: 1});
BlockSchema.index({timeNormalized: 1});
BlockSchema.index({previousBlockHash: 1});

BlockSchema.statics.addBlock = function(block, callback){
  let header = block.header.toObject();
  let blockTime = header.time * 1000;
  let blockTimeNormalized;
  let height;
  async.series([
    function(cb){
      Block.handleReorg(header, cb);
    },
    function(cb){
        Block.findOne({ hash: header.prevHash}, function(err, previousBlock) {
        if(err) {
          return cb(err);
        }
        blockTimeNormalized = blockTime;
        if(previousBlock && blockTime <= previousBlock.timeNormalized.getTime()) {
          blockTimeNormalized = previousBlock.timeNormalized.getTime() + 1;
        }
        height = (previousBlock && previousBlock.height + 1) || 1;
        Block.update({hash: header.hash}, {
          network: config.network,
          height: height,
          version: header.version,
          previousBlockHash: header.prevHash,
          merkleRoot: header.merkleRoot,
          time: new Date(blockTime),
          timeNormalized: new Date(blockTimeNormalized),
          bits: header.bits,
          nonce: header.nonce,
          transactionCount: block.transactions.length,
          size: block.toBuffer().length,
          reward: block.transactions[0].outputAmount
        }, {upsert: true}, function(err){
          if (err){
            return cb(err);
          }
          if (!previousBlock){
            return cb();
          }
          previousBlock.nextBlockHash = header.hash;
          previousBlock.save(cb);
        });
      });
    },
    function (cb) {
      async.eachLimit(block.transactions, config.maxPoolSize, function (tx, txCb) {
        Transaction.mintCoins({
          transaction: tx,
          network: config.network,
          mintHeight: height
        }, txCb);
      }, cb);
    },
    function (cb) {
      async.eachLimit(block.transactions, config.maxPoolSize, function (tx, txCb) {
        Transaction.spendCoins({
          transaction: tx,
          spentHeight: height
        }, txCb);
      }, cb);
    },
    function (cb) {
      async.eachLimit(block.transactions, config.maxPoolSize, function (tx, txCb) {
        Transaction.addTransaction({
          transaction: tx,
          network: config.network,
          blockHash: header.hash,
          blockHeight: height,
          blockTime: blockTime,
          blockTimeNormalized: blockTimeNormalized
        }, txCb);
      }, cb);
    }
  ], function(err){
    if(err){
      return callback(err);
    }
    Block.update({hash: header.hash}, {$set: {processed:true}}, callback);
  });
};

BlockSchema.statics.getPoolInfo = function(coinbase){
  //TODO need to make this actually parse the coinbase input and map to miner strings
  // also should go somewhere else
  return 'miningPool';
};

BlockSchema.statics.getLocalTip = function(callback) {
  Block.find({processed: true, network: config.network}).sort({height: -1}).limit(1).exec(function(err, bestBlock) {
    if(err) {
      return callback(err);
    }
    bestBlock = bestBlock[0] || {height: 0};
    callback(null, bestBlock);
  });
};

BlockSchema.statics.getLocatorHashes = function(callback) {
  Block.find({processed: true, network: config.network}).sort({height: -1}).limit(30).exec(function(err, locatorBlocks) {
    if(err) {
      return callback(err);
    }
    if(locatorBlocks.length < 2) {
      return callback(null, [Array(65).join('0')]);
    }
    locatorBlocks = _.pluck(locatorBlocks, 'hash');
    callback(null, locatorBlocks);
  });
};

BlockSchema.statics.handleReorg = function(header, callback) {
  Block.getLocalTip(function(err, localTip) {
    if(err) {
      return callback(err);
    }
    if (header && localTip.hash === header.prevHash) {
      return callback();
    }
    if(localTip.height === 0){
      return callback();
    }
    console.log(`Resetting tip to ${localTip.previousBlockHash}`);
    async.series([
      function(cb){
        Block.remove({ network: config.network, height: { $gte: localTip.height } }, cb);
      },
      function(cb){
        Transaction.remove({ network: config.network, blockHeight: { $gte: localTip.height } }, cb);
      },
      function(cb){
        Coin.remove({ network: config.network, mintHeight: { $gte: localTip.height } }, cb);
      },
      function (cb) {
        Coin.update({ network: config.network, spentHeight: { $gte: localTip.height } }, {
          $set: {spentTxid: null, spentHeight: -1}
        }, { multi: true }, cb);
      }
    ], callback);
  });
};

BlockSchema.statics._apiTransform = function(block, options){
  let transform = {
    hash: block.hash,
    height: block.height,
    version: block.version,
    size: block.size,
    merkleRoot: block.merkleRoot,
    time: block.time,
    timeNormalized: block.timeNormalized,
    nonce: block.nonce,
    bits: block.bits,
    difficulty: block.difficulty,
    chainWork: block.chainWork,
    previousBlockHash: block.previousBlockHash,
    nextBlockHash: block.nextBlockHash,
    reward: block.reward,
    isMainChain: block.mainChain,
    minedBy: Block.getPoolInfo(block.minedBy)
  };
  if(options && options.object){
    return transform;
  }
  return JSON.stringify(transform);
};

var Block = module.exports = mongoose.model('Block', BlockSchema);<|endoftext|>


<commit_before>var _ = require('underscore');
var async = require('async');

var Sheet = module.exports = function(id,db){
  this.id = id;
  this.db = db;
  this._data = null;
}

Sheet.prototype.rowAt = function(index,cb){
  var sheet = this;
  index = Number(index);
  sheet.db.getSheet(this.id,function(err,data){
    if(err) cb(err);
    if(index >= data.rows.length) return cb(new Error("row index out of bounds"));
    cb(null,data.rows[index]);
  });
};

Sheet.prototype.colAt = function(index,cb){
  var sheet = this;
  index = Number(index);
  sheet.db.getSheet(this.id,function(err,data){
    if(err) cb(err);
    if(index >= data.cols.length) return cb(new Error("col index out of bounds"));
    cb(null,data.cols[index]);
  });
};

Sheet.prototype.commitCell = function(row,col,cell,cb){
  this.updateCell(row,col,cell,cb);
};

Sheet.prototype.resizeCell = function(row_id,col_id,width,height,cb){
  var sheet = this;

  function setColWidth(next){
    sheet.setColWidth(col_id,width,next);
  }

  function setRowHeight(next){
    sheet.setRowHeight(row_id,height,next);
  }

  function done(err){
    if(err) throw err;
    cb(null,row_id,col_id,width,height);
  }

  async.parallel(
    [ setColWidth,
      setRowHeight ],
    done);
};

Sheet.prototype.setColWidth = function(col_id,width,cb){
  if(!_.isNumber(width)) return cb(null);
  var sheet = this;

  sheet.db.getColWidths(sheet.id,function(err,widths){
    if(err) throw err;
    widths = widths || {};
    widths[col_id] = width;
    sheet.db.setColWidths(sheet.id,widths,cb);
  });
};

Sheet.prototype.setRowHeight = function(row_id,height,cb){
  if(!_.isNumber(height)) return cb(null);
  var sheet = this;

  sheet.db.getColWidths(sheet.id,function(err,heights){
    if(err) throw err;
    heights = heights || {};
    heights[row_id] = height;
    sheet.db.setRowHeights(sheet.id,heights,cb);
  });
};

Sheet.prototype.updateCell = function(row,col,cell,cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    if(!data.cells[row]) data.cells[row] = {};
    data.cells[row][col] = cell;
    sheet.db.setCells(sheet.id,data.cells,cb);
  });

};

Sheet.prototype.getValue = function(row,col,cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    if(!data.cells[row]) return cb(null,'');
    cb(null, data.cells[row][col] || '');
  });
};

Sheet.prototype.getSheet = function(id,cb){
  var sheet = this;
  if(sheet._data) return cb(null,this._data);
  sheet.db.getSheet(this.id,function(err,data){
    sheet._data = data;
    cb(null,sheet._data);
  });
};

Sheet.prototype.insertCol = function(position, new_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    data.cols.splice(position,0,new_id);
    sheet.db.setColIndex(sheet.id,data.cols,cb);
  });
};

Sheet.prototype.insertRow = function(position, new_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    data.rows.splice(position,0,new_id);
    sheet.db.setRowIndex(sheet.id,data.rows,cb);
  });
};

function getCell(cells,row_id,col_id){
  if(!cells[row_id]) return undefined;
  if(_.isUndefined(cells[row_id])) return null;
  if(!cells[row_id][col_id]) return undefined;
  return cells[row_id][col_id] || null;
}

function getCellValue(cells,row_id,col_id){
  var value;
  var cell = getCell(cells,row_id,col_id);

  if(!cell || _.isUndefined(cell) || _.isNull(cell)){
    value = '';
  } else {
    value = cell.value;
  }

  if(!value || _.isUndefined(value) || _.isNull(value)) value = '';
  return value.toString().trim();
}

Sheet.prototype.sortRows = function(col_id, cb){
  var sheet = this;

  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    var cells = data.cells;
    var rows = data.rows;

    rows.sort(function compareRows(row_a,row_b){
      var value_a = getCellValue(cells,row_a,col_id);
      var value_b = getCellValue(cells,row_b,col_id);

      // blanks go last
      if (value_a === "" && value_b === "") return 0;
      if (value_a === "") return 1;
      if (value_b === "") return -1;

      // default sort
      return value_a.localeCompare(value_b);
    });

    sheet.db.setRowIndex(sheet.id,rows,function(){
      if(err) throw err;
      cb(null,col_id);
    });
  });
};

Sheet.prototype.deleteCol = function(col_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    var col_pos = _.indexOf(data.cols,col_id);
    if(col_pos === -1) return false;
    _.each(data.rows,function(row_id){
      if(data.cells[row_id]){
         delete data.cells[row_id][col_id];
      }
    });
    data.cols.splice(col_pos,1);
    sheet.db.setColIndex(sheet.id,data.cols);
    sheet.db.setCells(sheet.id,data.cells,cb);
  });
};

Sheet.prototype.deleteRow = function(row_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    var row_pos = _.indexOf(data.rows,row_id);
    if(row_pos === -1) return false;
    data.cells[row_id] = {};
    data.rows.splice(row_pos,1);
    sheet.db.setRowIndex(sheet.id,data.rows);
    sheet.db.setCells(sheet.id,data.cells,cb);
  });
};

Sheet.getCellType = function(cell_value){
  if(_.isNumber(cell_value)) return 'number';
  if(cell_value.charAt(0) == '=') return 'formula';
  if(_.isString(cell_value) && _.isNaN(cell_value * 1)) return 'string';
  if(_.isString(cell_value) && _.isFinite(cell_value * 1)) return 'number';
  throw 'Undefined Cell Type ' + cell_value;
};
<commit_msg>fixing problem with commiting cells<commit_after>var _ = require('underscore');
var async = require('async');

var Sheet = module.exports = function(id,db){
  this.id = id;
  this.db = db;
  this._data = null;
}

Sheet.prototype.rowAt = function(index,cb){
  var sheet = this;
  index = Number(index);
  sheet.db.getSheet(this.id,function(err,data){
    if(err) cb(err);
    if(index >= data.rows.length) return cb(new Error("row index out of bounds"));
    cb(null,data.rows[index]);
  });
};

Sheet.prototype.colAt = function(index,cb){
  var sheet = this;
  index = Number(index);
  sheet.db.getSheet(this.id,function(err,data){
    if(err) cb(err);
    if(index >= data.cols.length) return cb(new Error("col index out of bounds"));
    cb(null,data.cols[index]);
  });
};

Sheet.prototype.commitCell = function(row,col,cb){
  cb(null,row,col);
};

Sheet.prototype.resizeCell = function(row_id,col_id,width,height,cb){
  var sheet = this;

  function setColWidth(next){
    sheet.setColWidth(col_id,width,next);
  }

  function setRowHeight(next){
    sheet.setRowHeight(row_id,height,next);
  }

  function done(err){
    if(err) throw err;
    cb(null,row_id,col_id,width,height);
  }

  async.parallel(
    [ setColWidth,
      setRowHeight ],
    done);
};

Sheet.prototype.setColWidth = function(col_id,width,cb){
  if(!_.isNumber(width)) return cb(null);
  var sheet = this;

  sheet.db.getColWidths(sheet.id,function(err,widths){
    if(err) throw err;
    widths = widths || {};
    widths[col_id] = width;
    sheet.db.setColWidths(sheet.id,widths,cb);
  });
};

Sheet.prototype.setRowHeight = function(row_id,height,cb){
  if(!_.isNumber(height)) return cb(null);
  var sheet = this;

  sheet.db.getColWidths(sheet.id,function(err,heights){
    if(err) throw err;
    heights = heights || {};
    heights[row_id] = height;
    sheet.db.setRowHeights(sheet.id,heights,cb);
  });
};

Sheet.prototype.updateCell = function(row,col,cell,cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    if(!data.cells[row]) data.cells[row] = {};
    data.cells[row][col] = cell;
    sheet.db.setCells(sheet.id,data.cells,cb);
  });

};

Sheet.prototype.getValue = function(row,col,cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    if(!data.cells[row]) return cb(null,'');
    cb(null, data.cells[row][col] || '');
  });
};

Sheet.prototype.getSheet = function(id,cb){
  var sheet = this;
  if(sheet._data) return cb(null,this._data);
  sheet.db.getSheet(this.id,function(err,data){
    sheet._data = data;
    cb(null,sheet._data);
  });
};

Sheet.prototype.insertCol = function(position, new_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    data.cols.splice(position,0,new_id);
    sheet.db.setColIndex(sheet.id,data.cols,cb);
  });
};

Sheet.prototype.insertRow = function(position, new_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    data.rows.splice(position,0,new_id);
    sheet.db.setRowIndex(sheet.id,data.rows,cb);
  });
};

function getCell(cells,row_id,col_id){
  if(!cells[row_id]) return undefined;
  if(_.isUndefined(cells[row_id])) return null;
  if(!cells[row_id][col_id]) return undefined;
  return cells[row_id][col_id] || null;
}

function getCellValue(cells,row_id,col_id){
  var value;
  var cell = getCell(cells,row_id,col_id);

  if(!cell || _.isUndefined(cell) || _.isNull(cell)){
    value = '';
  } else {
    value = cell.value;
  }

  if(!value || _.isUndefined(value) || _.isNull(value)) value = '';
  return value.toString().trim();
}

Sheet.prototype.sortRows = function(col_id, cb){
  var sheet = this;

  sheet.db.getSheet(this.id,function(err,data){
    if(err) throw err;
    var cells = data.cells;
    var rows = data.rows;

    rows.sort(function compareRows(row_a,row_b){
      var value_a = getCellValue(cells,row_a,col_id);
      var value_b = getCellValue(cells,row_b,col_id);

      // blanks go last
      if (value_a === "" && value_b === "") return 0;
      if (value_a === "") return 1;
      if (value_b === "") return -1;

      // default sort
      return value_a.localeCompare(value_b);
    });

    sheet.db.setRowIndex(sheet.id,rows,function(){
      if(err) throw err;
      cb(null,col_id);
    });
  });
};

Sheet.prototype.deleteCol = function(col_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    var col_pos = _.indexOf(data.cols,col_id);
    if(col_pos === -1) return false;
    _.each(data.rows,function(row_id){
      if(data.cells[row_id]){
         delete data.cells[row_id][col_id];
      }
    });
    data.cols.splice(col_pos,1);
    sheet.db.setColIndex(sheet.id,data.cols);
    sheet.db.setCells(sheet.id,data.cells,cb);
  });
};

Sheet.prototype.deleteRow = function(row_id, cb){
  var sheet = this;
  sheet.db.getSheet(this.id,function(err,data){
    var row_pos = _.indexOf(data.rows,row_id);
    if(row_pos === -1) return false;
    data.cells[row_id] = {};
    data.rows.splice(row_pos,1);
    sheet.db.setRowIndex(sheet.id,data.rows);
    sheet.db.setCells(sheet.id,data.cells,cb);
  });
};

Sheet.getCellType = function(cell_value){
  if(_.isNumber(cell_value)) return 'number';
  if(cell_value.charAt(0) == '=') return 'formula';
  if(_.isString(cell_value) && _.isNaN(cell_value * 1)) return 'string';
  if(_.isString(cell_value) && _.isFinite(cell_value * 1)) return 'number';
  throw 'Undefined Cell Type ' + cell_value;
};
<|endoftext|>


<commit_before>utils = require('../utils');

//see http://en.wikipedia.org/wiki/Box-drawing_characters
exports.plugin = function(cli)
{

	//┌──
	//├──
	//└──

	var branch = utils.repeat('─',1),
	tabs = utils.repeat(' ',branch.length+2);

	cli.drawTree = cli.tree = function(tree, ops, tab)
	{
		if(!ops) ops = {};

		if(!tab) tab = '';

		var n = utils.objectSize(tree),
		i = 0,
		printedBreak = false;

		for(var index in tree)
		{

			var childrenOrValue = tree[index],
			toc = typeof childrenOrValue,
			//tol = typeof labelOrIndex,
			edge = i < n-1 ? '├' : '└';

			console.log('%s%s%s %s', tab, edge, branch, toc == 'string' ? index + ': ' + childrenOrValue : index);

			if(toc == 'object')
			{
				printedBreak = cli.drawTree(childrenOrValue, ops, n > 1 && i < n-1 ? tab + '|' + tabs.substr(1) : tab + tabs);
			}
			/*else
			if(toc == 'string' || toc == 'number')
			{

			}*/

			 i++;
		}

		//add extra breaks for folders - a little more readable
		if(!printedBreak)
		{
			console.log('%s',tab);
			printedBreak = true;
		}

		return printedBreak;
	}
}<commit_msg>fix printing for arrays<commit_after>utils = require('../utils');

//see http://en.wikipedia.org/wiki/Box-drawing_characters
exports.plugin = function(cli)
{

	//┌──
	//├──
	//└──

	var branch = utils.repeat('─',1),
	tabs = utils.repeat(' ',branch.length+2);

	cli.drawTree = cli.tree = function(tree, ops, tab)
	{
		if(!ops) ops = {};

		if(!tab) tab = '';

		var n = utils.objectSize(tree),
		i = 0,
		printedBreak = false;

		for(var index in tree)
		{

			var childrenOrValue = tree[index],
			toc = typeof childrenOrValue,
			toi = typeof index,
			edge = i < n-1 ? '├' : '└';

			var value = !isNaN(Number(index)) ?  childrenOrValue : index + ': ' + childrenOrValue;

			console.log('%s%s%s %s', tab, edge, branch, toc == 'string' ? value: index);

			if(toc == 'object')
			{
				printedBreak = cli.drawTree(childrenOrValue, ops, n > 1 && i < n-1 ? tab + '|' + tabs.substr(1) : tab + tabs);
			}
			/*else
			if(toc == 'string' || toc == 'number')
			{

			}*/

			 i++;
		}

		//add extra breaks for folders - a little more readable
		if(!printedBreak)
		{
			console.log('%s',tab);
			printedBreak = true;
		}

		return printedBreak;
	}
}<|endoftext|>


<commit_before>
var color = exports.color = require("./ansi-color");

global.proto = require('./proto').proto;
global.exec = require('./exec').exec;
global.Timer = require('./exec').Timer;
global.once = require('./once').once;
exports.onShutdown = require('./exec').onShutdown;

function throttled(cb, delay) {
  var timeout
    , fire = false;
  return function() {
    if (!timeout) {
      cb();
      timeout = setTimeout(function() {
        if (fire) { cb(); }
        timeout = undefined;
        fire = false;
      }, delay);
    } else {
      fire = true;
    }
  };
}

global.toInt = function(x) {
  return parseInt(x + '', 10);
};

var util = require('util');
global.debug = function(x, name) {
  global.error((name ? name + ': ' : '') + util.inspect(x));
};

var events  = require('events');
var logger = new events.EventEmitter();
exports.logger = logger;

global.error = function(s) {
  logger.emit('data', 'error', s);
  console.error(color.set(s, "red"));
};

global.warn = function(s) {
  logger.emit('data', 'warn', s);
  console.log(color.set(s, "yellow"));
};

global.info = function(s) {
  logger.emit('data', 'info', s);
  console.log(color.set(s, "black", true));
};

global.verbose = function(s) {
  logger.emit('data', 'verbose', s);
  //console.log(color.set(s, "black", true));
};

global.throttled = throttled;

var errorEvents = new events.EventEmitter();

global.errorHandler = function(optionalMessage, cbOnError, cbOnSuccess) {
  if (!cbOnSuccess) {
    // The optional message was omitted
    cbOnSuccess = cbOnError;
    cbOnError = optionalMessage;
    optionalMessage = 'ERR';
  }
  return function(err, arg0, arg1) {
    if (err) {
      var msg = optionalMessage.replace('ERR', err).replace('ARG0', arg0).replace('ARG1', arg1);
      errorEvents.emit('err', msg);
      cbOnError(msg);
    } else {
      cbOnSuccess.apply(this, arguments);
    }
  };
};

global.errorHandler.on = function() {
  errorEvents.on.apply(errorEvents, arguments);
};<commit_msg>autocommit<commit_after>
var color = exports.color = require("./ansi-color");

global.proto = require('./proto').proto;
global.exec = require('./exec').exec;
global.Timer = require('./exec').Timer;
global.once = require('./once').once;
exports.onShutdown = require('./exec').onShutdown;

function throttled(cb, delay) {
  var timeout
    , fire = false
    , lastfire = false;
  function time() {
    return (new Date()).getTime();
  }
  return function() {
    if (!timeout) {
      var nextDelay = delay - (time() - lastfire);
      if (nextDelay <= 0) {
        cb();
        lastfire = time();
      } else {
        timeout = setTimeout(function() {
          if (fire) {
            cb();
            lastfire = time();
          }
          timeout = undefined;
          fire = false;
        }, nextDelay);
      }
    } else {
      fire = true;
    }
  };
}

global.toInt = function(x) {
  return parseInt(x + '', 10);
};

var util = require('util');
global.debug = function(x, name) {
  global.error((name ? name + ': ' : '') + util.inspect(x));
};

var events  = require('events');
var logger = new events.EventEmitter();
exports.logger = logger;

global.error = function(s) {
  logger.emit('data', 'error', s);
  console.error(color.set(s, "red"));
};

global.warn = function(s) {
  logger.emit('data', 'warn', s);
  console.log(color.set(s, "yellow"));
};

global.info = function(s) {
  logger.emit('data', 'info', s);
  console.log(color.set(s, "black", true));
};

global.verbose = function(s) {
  logger.emit('data', 'verbose', s);
  //console.log(color.set(s, "black", true));
};

global.throttled = throttled;

var errorEvents = new events.EventEmitter();

global.errorHandler = function(optionalMessage, cbOnError, cbOnSuccess) {
  if (!cbOnSuccess) {
    // The optional message was omitted
    cbOnSuccess = cbOnError;
    cbOnError = optionalMessage;
    optionalMessage = 'ERR';
  }
  return function(err, arg0, arg1) {
    if (err) {
      var msg = optionalMessage.replace('ERR', err).replace('ARG0', arg0).replace('ARG1', arg1);
      errorEvents.emit('err', msg);
      cbOnError(msg);
    } else {
      cbOnSuccess.apply(this, arguments);
    }
  };
};

global.errorHandler.on = function() {
  errorEvents.on.apply(errorEvents, arguments);
};<|endoftext|>


<commit_before>const databaseVersions = [
	{
		targetVersion: '6.0.0',
		queries: [
			'ALTER TABLE yamero_counter RENAME TO main_counter;',
		]
	},
	{
		targetVersion: '7.0.2',
		queries: [
			'UPDATE sounds SET filename = "objection", displayname = "Objection!!" WHERE filename = "igiari";',
		],
		notes: [
			'Rename \'igiari.mp3\' in the sounds folder to \'objection.mp3\' or the sound will not work on-site anymore.'
		]
	},
	{
		targetVersion: '8.0.0',
		queries: [
			`CREATE TABLE IF NOT EXISTS meta (
				version TEXT NOT NULL
			);`,
			'INSERT OR IGNORE INTO meta ( version ) VALUES ( "8.0.0" );',
			`CREATE TABLE IF NOT EXISTS sounds_temp (
				id INTEGER PRIMARY KEY,
				filename TEXT NOT NULL UNIQUE,
				displayname TEXT DEFAULT NULL,
				source TEXT DEFAULT NULL,
				count INTEGER NOT NULL DEFAULT 0,
				theme TEXT DEFAULT "megumin"
			);`,
			'INSERT INTO sounds_temp ( id, filename, displayname, source, count ) SELECT * FROM sounds;',
			'DROP TABLE sounds;',
			'ALTER TABLE sounds_temp RENAME TO sounds;',
			'UPDATE sounds SET filename = "kazuma_megumin" WHERE filename = "kazuma";',
			'UPDATE sounds SET theme = "megumin" WHERE theme IS NULL;',
			`INSERT INTO sounds ( filename, displayname, source, count, theme ) VALUES
			( "hikineet_aqua", "Shut-in NEET", "Season 1", 0, "aqua" ),
			( "godblow", "God Blow", "Season 1", 0, "aqua" ),
			( "godrequiem", "God Requiem", "Season 1", 0, "aqua" ),
			( "kazuma_aqua", "Kazuma", "Season 1", 0, "aqua" ),
			( "kazumasan", "Kazuma-san", "Season 1", 0, "aqua" ),
			( "sacredbreakspell", "Sacred Break Spell", "Season 1", 0, "aqua" ),
			( "purification", "Purification", "Season 1", 0, "aqua" ),
			( "turnundead", "Turn Undead", "Season 1", 0, "aqua" ),
			( "nomercy", "No mercy", "Season 1", 0, "darkness" ),
			( "whattodo", "What to do", "Season 1", 0, "darkness" ),
			( "kazuma_darkness", "Kazuma", "Season 1", 0, "darkness" ),
			( "useless", "She's useless", "Season 1", 0, "kazuma" ),
			( "hikineet_kazuma", "Shut-in NEET", "Season 1", 0, "kazuma" ),
			( "calmdown", "Calm down", "Season 1", 0, "kazuma" ),
			( "steal", "Steal", "Season 1", 0, "kazuma" ),
			( "thehell", "The hell's this?!", "Season 1", 0, "kazuma" ),
			( "createwater", "Create Water", "Season 1", 0, "kazuma" ),
			( "nice-explosion", "Nice explosion", "Season 1", 0, "kazuma" ),
			( "kazuma_kazuma", "Kazuma", "Season 1", 0, "kazuma" ),
			( "treasure", "Treasure", "Season 2", 0, "aqua" ),
			( "sacredexorcism", "Sacred Exorcism", "Season 2", 0, "aqua" ),
			( "forcefire", "Force Fire", "Season 2", 0, "aqua" ),
			( "snipe", "Snipe", "Season 2", 0, "kazuma" ),
			( "freeze", "Freeze", "Season 2", 0, "kazuma" ),
			( "lurk", "Lurk", "Season 2", 0, "kazuma" ),
			( "damnit", "Damn it!", "Season 2", 0, "kazuma" ),
			( "wahaha", "Wahaha", "Season 2", 0, "megumin" );
			`,
			`CREATE TABLE IF NOT EXISTS milestones (
				id INTEGER PRIMARY KEY,
				count INTEGER NOT NULL UNIQUE,
				reached INTEGER NOT NULL DEFAULT 0,
				timestamp INTEGER DEFAULT NULL,
				sound_id INTEGER DEFAULT NULL,
					FOREIGN KEY(sound_id) REFERENCES sounds(id) ON UPDATE CASCADE ON DELETE SET NULL
			);`,
			`CREATE TABLE IF NOT EXISTS statistics_temp (
				id INTEGER PRIMARY KEY,
				date TEXT NOT NULL UNIQUE,
				count INTEGER NOT NULL DEFAULT 0
			);`,
			'INSERT INTO statistics_temp SELECT * FROM statistics;',
			'DROP TABLE statistics;',
			'ALTER TABLE statistics_temp RENAME TO statistics;'
		]
	},
];

const { Database } = require('sqlite3');
const { join } = require('path');
const { createInterface } = require('readline');
const { copyFile } = require('fs');
const { databasePath } = require('./src/config.json');

const db = new Database(join('src', databasePath));
let currentVersion, newVersion, force;

const newVersionCLI = process.argv.filter(arg => arg.includes('newVersion='))[0];
const currentVersionCLI = process.argv.filter(arg => arg.includes('currentVersion='))[0];
const forceCLI = process.argv.filter(arg => arg.includes('--force'))[0];

if (newVersionCLI) newVersion = newVersionCLI.substr(newVersionCLI.indexOf('=') + 1).trim();
if (currentVersionCLI) currentVersion = currentVersionCLI.substr(currentVersionCLI.indexOf('=') + 1).trim();
if (forceCLI) force = true;

console.log('This is the megumin.love database migration tool.');
console.log('------------------------------------');
console.log('Creating database backup.');

copyFile(db.filename, `${db.filename}.bak`, err => {
	if (err) {
		console.log('Database backup creation failed, migration aborting.');
		return setTimeout(() => process.exit(1), 2000);
	}
});

if (!currentVersion) {
	db.get('SELECT version FROM meta', [], (selectErr, data) => {
		if (selectErr || !data || (data && !data.version)) currentVersion = 'Unknown';
		else currentVersion = data.version;

		console.log('------------------------------------');
		console.log(`Detected database version: ${currentVersion}`);

		if (currentVersion === 'Unknown') {
			console.log('\nPlease enter your version of megumin.love before the update (e.g. 6.0.2 or 7.1.0).');

			const currentVersionInput = createInterface({
				input: process.stdin,
				output: process.stdout,
				prompt: 'Starting database version: '
			});

			currentVersionInput.prompt();

			currentVersionInput.on('line', ver => {
				currentVersion = ver.trim();
				currentVersionInput.close();
			});

			currentVersionInput.on('close', () => {
				if (!newVersion) promptNewVersion();
				else executeUpdateQueries();
			});
		}
		else {
			if (!newVersion) promptNewVersion(); // eslint-disable-line no-lonely-if
			else executeUpdateQueries();
		}
	});
}
else {
	if (!newVersion) promptNewVersion(); // eslint-disable-line no-lonely-if
	else executeUpdateQueries();
}

function promptNewVersion() {
	const newVersionInput = createInterface({
		input: process.stdin,
		output: process.stdout,
		prompt: '\nTargeted database version (\'x.x.x\' or \'latest\'): '
	});

	newVersionInput.prompt();

	newVersionInput.on('line', ver => {
		newVersion = ver.trim();
		newVersionInput.close();

		executeUpdateQueries();
	});
}

function executeUpdateQueries() {
	if (newVersion === 'latest') newVersion = databaseVersions[databaseVersions.length - 1].targetVersion;

	if (currentVersion > newVersion && !force) {
		console.log('\nCurrent version is above new version. No updating is necessary.');
		return process.exit(0);
	}
	else if (currentVersion === newVersion && !force) {
		console.log('\nCurrent version equals new version. No updating is necessary.');
		return process.exit(0);
	}

	const upgrades = databaseVersions.filter(version => {
		const aboveCurrent = currentVersion < version.targetVersion;
		const belowNew = newVersion >= version.targetVersion;

		if (force) return currentVersion <= version.targetVersion;

		return aboveCurrent && belowNew;
		// Passes all versions above the current if the latest version is aspired
	});

	upgrades.forEach(version => {
		console.log('------------------------------------');
		console.log(`Running database queries required for version ${version.targetVersion}.`);

		const queries = version.queries.join(' ');

		db.exec(queries, err => {
			if (err) {
				console.log(`An error occurred while running the queries for version ${version.targetVersion}:`);
				console.log(err);
			}
		});

		if (version.notes) version.notes.forEach(note => console.log('Notice:', note));
		console.log(`Migration to version ${version.targetVersion} completed.`);
	});

	console.log('------------------------------------');
	console.log('Please review the above output for important notes and restore the created database backup if anything went wrong.');
}<commit_msg>Adjust values<commit_after>const databaseVersions = [
	{
		targetVersion: '6.0.0',
		queries: [
			'ALTER TABLE yamero_counter RENAME TO main_counter;',
		]
	},
	{
		targetVersion: '7.0.2',
		queries: [
			'UPDATE sounds SET filename = "objection", displayname = "Objection!!" WHERE filename = "igiari";',
		],
		notes: [
			'Rename \'igiari.mp3\' in the sounds folder to \'objection.mp3\' or the sound will not work on-site anymore.'
		]
	},
	{
		targetVersion: '8.0.0',
		queries: [
			`CREATE TABLE IF NOT EXISTS meta (
				version TEXT NOT NULL
			);`,
			'INSERT OR IGNORE INTO meta ( version ) VALUES ( "8.0.0" );',
			`CREATE TABLE IF NOT EXISTS sounds_temp (
				id INTEGER PRIMARY KEY,
				filename TEXT NOT NULL UNIQUE,
				displayname TEXT DEFAULT NULL,
				source TEXT DEFAULT NULL,
				count INTEGER NOT NULL DEFAULT 0,
				theme TEXT DEFAULT "megumin"
			);`,
			'INSERT INTO sounds_temp ( id, filename, displayname, source, count ) SELECT * FROM sounds;',
			'DROP TABLE sounds;',
			'ALTER TABLE sounds_temp RENAME TO sounds;',
			'UPDATE sounds SET filename = "kazuma_megumin" WHERE filename = "kazuma";',
			'UPDATE sounds SET theme = "megumin" WHERE theme IS NULL;',
			`INSERT INTO sounds ( filename, displayname, source, count, theme ) VALUES
			( "hikineet_aqua", "Shut-in NEET", "Season 1", 0, "aqua" ),
			( "godblow", "God Blow", "Season 1", 0, "aqua" ),
			( "godrequiem", "God Requiem", "Season 1", 0, "aqua" ),
			( "kazuma_aqua", "Kazuma", "Season 1", 0, "aqua" ),
			( "kazumasan", "Kazuma-san", "Season 1", 0, "aqua" ),
			( "sacredbreakspell", "Sacred Break Spell", "Season 1", 0, "aqua" ),
			( "purification", "Purification", "Season 1", 0, "aqua" ),
			( "turnundead", "Turn Undead", "Season 1", 0, "aqua" ),
			( "nomercy", "No mercy", "Season 1", 0, "darkness" ),
			( "whattodo", "What to do", "Season 1", 0, "darkness" ),
			( "kazuma_darkness", "Kazuma", "Season 1", 0, "darkness" ),
			( "useless", "She's useless", "Season 1", 0, "kazuma" ),
			( "hikineet_kazuma", "Shut-in NEET", "Season 1", 0, "kazuma" ),
			( "calmdown", "Calm down", "Season 1", 0, "kazuma" ),
			( "steal", "Steal", "Season 1", 0, "kazuma" ),
			( "thehell", "The hell's this?!", "Season 1", 0, "kazuma" ),
			( "createwater", "Create Water", "Season 1", 0, "kazuma" ),
			( "nice-explosion", "Nice explosion", "Season 1", 0, "kazuma" ),
			( "kazuma_kazuma", "Kazuma", "Season 1", 0, "kazuma" ),
			( "treasure", "Treasure", "Season 2", 0, "aqua" ),
			( "sacredexorcism", "Sacred Exorcism", "Season 2", 0, "aqua" ),
			( "forcefire", "Force Fire", "Season 2", 0, "aqua" ),
			( "snipe", "Snipe", "Season 2", 0, "kazuma" ),
			( "freeze", "Freeze", "Season 2", 0, "kazuma" ),
			( "lurk", "Lurk", "Season 2", 0, "kazuma" ),
			( "damnit", "Damn it!", "Season 2", 0, "kazuma" ),
			( "wahaha", "Wahaha", "Season 2", 0, "megumin" );
			`,
			'UPDATE sounds SET source = NULL where filename = "realname";',
			'UPDATE sounds SET displayname = NULL where filename = "realname";',
			`CREATE TABLE IF NOT EXISTS milestones (
				id INTEGER PRIMARY KEY,
				count INTEGER NOT NULL UNIQUE,
				reached INTEGER NOT NULL DEFAULT 0,
				timestamp INTEGER DEFAULT NULL,
				sound_id INTEGER DEFAULT NULL,
					FOREIGN KEY(sound_id) REFERENCES sounds(id) ON UPDATE CASCADE ON DELETE SET NULL
			);`,
			`CREATE TABLE IF NOT EXISTS statistics_temp (
				id INTEGER PRIMARY KEY,
				date TEXT NOT NULL UNIQUE,
				count INTEGER NOT NULL DEFAULT 0
			);`,
			'INSERT INTO statistics_temp SELECT * FROM statistics;',
			'DROP TABLE statistics;',
			'ALTER TABLE statistics_temp RENAME TO statistics;'
		]
	},
];

const { Database } = require('sqlite3');
const { join } = require('path');
const { createInterface } = require('readline');
const { copyFile } = require('fs');
const { databasePath } = require('./src/config.json');

const db = new Database(join('src', databasePath));
let currentVersion, newVersion, force;

const newVersionCLI = process.argv.filter(arg => arg.includes('newVersion='))[0];
const currentVersionCLI = process.argv.filter(arg => arg.includes('currentVersion='))[0];
const forceCLI = process.argv.filter(arg => arg.includes('--force'))[0];

if (newVersionCLI) newVersion = newVersionCLI.substr(newVersionCLI.indexOf('=') + 1).trim();
if (currentVersionCLI) currentVersion = currentVersionCLI.substr(currentVersionCLI.indexOf('=') + 1).trim();
if (forceCLI) force = true;

console.log('This is the megumin.love database migration tool.');
console.log('------------------------------------');
console.log('Creating database backup.');

copyFile(db.filename, `${db.filename}.bak`, err => {
	if (err) {
		console.log('Database backup creation failed, migration aborting.');
		return setTimeout(() => process.exit(1), 2000);
	}
});

if (!currentVersion) {
	db.get('SELECT version FROM meta', [], (selectErr, data) => {
		if (selectErr || !data || (data && !data.version)) currentVersion = 'Unknown';
		else currentVersion = data.version;

		console.log('------------------------------------');
		console.log(`Detected database version: ${currentVersion}`);

		if (currentVersion === 'Unknown') {
			console.log('\nPlease enter your version of megumin.love before the update (e.g. 6.0.2 or 7.1.0).');

			const currentVersionInput = createInterface({
				input: process.stdin,
				output: process.stdout,
				prompt: 'Starting database version: '
			});

			currentVersionInput.prompt();

			currentVersionInput.on('line', ver => {
				currentVersion = ver.trim();
				currentVersionInput.close();
			});

			currentVersionInput.on('close', () => {
				if (!newVersion) promptNewVersion();
				else executeUpdateQueries();
			});
		}
		else {
			if (!newVersion) promptNewVersion(); // eslint-disable-line no-lonely-if
			else executeUpdateQueries();
		}
	});
}
else {
	if (!newVersion) promptNewVersion(); // eslint-disable-line no-lonely-if
	else executeUpdateQueries();
}

function promptNewVersion() {
	const newVersionInput = createInterface({
		input: process.stdin,
		output: process.stdout,
		prompt: '\nTargeted database version (\'x.x.x\' or \'latest\'): '
	});

	newVersionInput.prompt();

	newVersionInput.on('line', ver => {
		newVersion = ver.trim();
		newVersionInput.close();

		executeUpdateQueries();
	});
}

function executeUpdateQueries() {
	if (newVersion === 'latest') newVersion = databaseVersions[databaseVersions.length - 1].targetVersion;

	if (currentVersion > newVersion && !force) {
		console.log('\nCurrent version is above new version. No updating is necessary.');
		return process.exit(0);
	}
	else if (currentVersion === newVersion && !force) {
		console.log('\nCurrent version equals new version. No updating is necessary.');
		return process.exit(0);
	}

	const upgrades = databaseVersions.filter(version => {
		const aboveCurrent = currentVersion < version.targetVersion;
		const belowNew = newVersion >= version.targetVersion;

		if (force) return currentVersion <= version.targetVersion;

		return aboveCurrent && belowNew;
		// Passes all versions above the current if the latest version is aspired
	});

	upgrades.forEach(version => {
		console.log('------------------------------------');
		console.log(`Running database queries required for version ${version.targetVersion}.`);

		const queries = version.queries.join(' ');

		db.exec(queries, err => {
			if (err) {
				console.log(`An error occurred while running the queries for version ${version.targetVersion}:`);
				console.log(err);
			}
		});

		if (version.notes) version.notes.forEach(note => console.log('Notice:', note));
		console.log(`Migration to version ${version.targetVersion} completed.`);
	});

	console.log('------------------------------------');
	console.log('Please review the above output for important notes and restore the created database backup if anything went wrong.');
}<|endoftext|>


<commit_before>/** @babel */

import {CompositeDisposable} from 'atom'

const treeViewDockSelector = 'atom-panel-container.left > atom-dock.left > .atom-dock-inner'
const tabBarPaddingSelector = `.platform-${process.platform}:not(.fullscreen):not(.hidden-title-bar)
  atom-panel-container.left + atom-workspace-axis.vertical
  .pane:nth-child(1)
  .tab-bar
`

var init = false
var paneSub = null
var configSubs = null
var mutationObserver = null
var treeViewDockElement = null
var treeViewVisible = false

function __guard__ (value, transform) {
  return (typeof value !== 'undefined' && value !== null)
    ? transform(value)
    : undefined
}

function setPadding (width) {
  __guard__(document.querySelector(tabBarPaddingSelector), element => {
    element.style.paddingLeft = width + 'px'
  })
}

function updatePadding () {
  if (treeViewVisible) {
    setPadding(0)
  } else {
    if (atom.config.get('no-title-bar.automaticTrafficLightsPadding')) {
      __guard__(document.querySelector('.tool-panel.tree-view'), element => {
        setPadding(element.clientWidth)
      })
      return
    }

    // always fallback to manual setting
    setPadding(atom.config.get('no-title-bar.trafficLightsPadding'))
  }
}

function mutationHandler (mutations) {
  __guard__(treeViewDockElement, x => {
    treeViewVisible = x.classList.contains('atom-dock-open')
    updatePadding()
  })
}

function destroyConfigSubs () {
  __guard__(configSubs, x => x.dispose())
  configSubs = null
}

function destroyPaneSub () {
  __guard__(paneSub, x => x.dispose())
  paneSub = null
}

function destroyMutationObserver () {
  __guard__(mutationObserver, x => x.disconnect())
  mutationObserver = null
}

function handlePaneItem (item) {
  if (!init && item.constructor.name === 'TextEditor') {
    init = true
    updatePadding()
  } else if (item.constructor.name === 'TreeView') {
    mutationObserver = new MutationObserver(mutationHandler)
    treeViewDockElement = document.querySelector(treeViewDockSelector)
    __guard__(treeViewDockElement, x => {
      destroyPaneSub()
      mutationObserver.observe(x, { attributes: true })
    })
  }
}

export function enabledForPlatform (platform) {
  return document.body.classList.contains(`platform-${platform}`)
}

export function activate (state) {
  if (enabledForPlatform(process.platform)) {
    paneSub = atom.workspace.observePaneItems(handlePaneItem)
    configSubs = new CompositeDisposable()
    configSubs.add(
      atom.config.observe('no-title-bar.trafficLightsPadding', () => { updatePadding() }),
      atom.config.observe('no-title-bar.automaticTrafficLightsPadding', () => { updatePadding() })
    )
    document.body.classList.add('no-title-bar')
  }
}

export function deactivate () {
  if (enabledForPlatform(process.platform)) {
    updatePadding(0)
    destroyPaneSub()
    destroyConfigSubs()
    destroyMutationObserver()
    document.body.classList.remove('no-title-bar')
  }
}

export const config = {
  trafficLightsPadding: {
    description: 'A padding width, in pixels, to accommodate the window traffic lights.',
    type: 'integer',
    minimum: 0,
    default: 80
  },
  automaticTrafficLightsPadding: {
    description: 'Use the width of your Tree View as the width for the traffic lights padding.',
    type: 'boolean',
    default: false
  }
}
<commit_msg>Fixes incorrect logic in updatePadding().<commit_after>/** @babel */

import {CompositeDisposable} from 'atom'

const treeViewDockSelector = 'atom-panel-container.left > atom-dock.left > .atom-dock-inner'
const tabBarPaddingSelector = `.platform-${process.platform}:not(.fullscreen):not(.hidden-title-bar)
  atom-panel-container.left + atom-workspace-axis.vertical
  .pane:nth-child(1)
  .tab-bar
`

var init = false
var paneSub = null
var configSubs = null
var mutationObserver = null
var treeViewDockElement = null
var treeViewVisible = false

function __guard__ (value, transform) {
  return (typeof value !== 'undefined' && value !== null)
    ? transform(value)
    : undefined
}

function setPadding (width) {
  __guard__(document.querySelector(tabBarPaddingSelector), element => {
    element.style.paddingLeft = width + 'px'
  })
  return true
}

function configTrafficLightsPadding () {
  return atom.config.get('no-title-bar.trafficLightsPadding')
}

function configAutomaticTrafficLightsPadding () {
  return atom.config.get('no-title-bar.automaticTrafficLightsPadding')
}

function updatePadding () {
  if (treeViewVisible) {
    return setPadding(0)
  }

  if (configAutomaticTrafficLightsPadding()) {
    const set = __guard__(document.querySelector('.tool-panel.tree-view'), element => {
      if (element.clientWidth > 0) {
        return setPadding(element.clientWidth)
      }
      return setPadding(configTrafficLightsPadding())
    })
    if (set) {
      return set
    }
  }

  // always fallback to config setting
  return setPadding(configTrafficLightsPadding())
}

function mutationHandler (mutations) {
  __guard__(treeViewDockElement, x => {
    treeViewVisible = x.classList.contains('atom-dock-open')
    updatePadding()
  })
}

function destroyConfigSubs () {
  __guard__(configSubs, x => x.dispose())
  configSubs = null
}

function destroyPaneSub () {
  __guard__(paneSub, x => x.dispose())
  paneSub = null
}

function destroyMutationObserver () {
  __guard__(mutationObserver, x => x.disconnect())
  mutationObserver = null
}

function handlePaneItem (item) {
  if (!init && item.constructor.name === 'TextEditor') {
    init = true
    updatePadding()
  } else if (item.constructor.name === 'TreeView') {
    mutationObserver = new MutationObserver(mutationHandler)
    treeViewDockElement = document.querySelector(treeViewDockSelector)
    __guard__(treeViewDockElement, x => {
      destroyPaneSub()
      mutationObserver.observe(x, { attributes: true })
    })
  }
}

export function enabledForPlatform (platform) {
  return document.body.classList.contains(`platform-${platform}`)
}

export function activate (state) {
  if (enabledForPlatform(process.platform)) {
    paneSub = atom.workspace.observePaneItems(handlePaneItem)
    configSubs = new CompositeDisposable()
    configSubs.add(
      atom.config.observe('no-title-bar.trafficLightsPadding', () => { updatePadding() }),
      atom.config.observe('no-title-bar.automaticTrafficLightsPadding', () => { updatePadding() })
    )
    document.body.classList.add('no-title-bar')
  }
}

export function deactivate () {
  if (enabledForPlatform(process.platform)) {
    updatePadding(0)
    destroyPaneSub()
    destroyConfigSubs()
    destroyMutationObserver()
    document.body.classList.remove('no-title-bar')
  }
}

export const config = {
  trafficLightsPadding: {
    description: 'A padding width, in pixels, to accommodate the window traffic lights.',
    type: 'integer',
    minimum: 0,
    default: 80
  },
  automaticTrafficLightsPadding: {
    description: 'Use the width of your Tree View as the width for the traffic lights padding.',
    type: 'boolean',
    default: false
  }
}
<|endoftext|>