<commit_before>import Realm from 'realm';

import currency from '../../localization/currency';
import { createRecord } from '../utilities';

/**
 * A stocktake batch.
 *
 * @property  {string}     id
 * @property  {Stocktake}  stocktake
 * @property  {ItemBatch}  itemBatch
 * @property  {number}     snapshotNumberOfPacks
 * @property  {number}     packSize
 * @property  {Date}       expiryDate
 * @property  {string}     batch
 * @property  {number}     costPrice
 * @property  {number}     sellPrice
 * @property  {number}     countedNumberOfPacks
 * @property  {number}     sortIndex
 */
export class StocktakeBatch extends Realm.Object {
  /**
   * Delete stocktake batch and associated item batch.
   *
   * @param  {Realm}  database
   */
  destructor(database) {
    if (this.snapshotNumberOfPacks === 0 && this.itemBatch.numberOfPacks === 0) {
      database.delete('ItemBatch', this.itemBatch);
    }
  }

  get currentLocationName() {
    return this.location?.description ?? '';
  }

  get currentVvmStatusName() {
    return this.vaccineVialMonitorStatus?.description ?? '';
  }

  get hasBreached() {
    return !!this.itemBatch?.hasBreached;
  }

  get otherPartyName() {
    return this.supplier?.name ?? '';
  }

  /**
   * @return {Bool} Indicator if this batch has a valid number of doses.
   */
  get hasValidDoses() {
    const { item } = this.itemBatch;
    const { doses: dosesPerVial } = item;

    const maximumDosesPossible = this.snapshotTotalQuantity * dosesPerVial;
    const minimumDosesPossible = this.snapshotTotalQuantity;

    const tooManyDoses = this.doses > maximumDosesPossible;
    const tooLittleDoses = this.doses < minimumDosesPossible;
    const justRightDoses = !tooManyDoses && !tooLittleDoses;

    return justRightDoses;
  }

  /**
   * Get snapshot of total quantity in batch.
   *
   * @return  {number}
   */
  get snapshotTotalQuantity() {
    return this.snapshotNumberOfPacks * this.packSize;
  }

  /**
   * Get the total countedQuantity for this batch. If nothing has been
   * counted, return 0.
   *
   * @return  {number}
   */
  get countedTotalQuantity() {
    if (this.countedNumberOfPacks === null) return 0;
    return this.countedNumberOfPacks * this.packSize;
  }

  /**
   * Get if stocktake will reduce stock on hand of item below zero.
   *
   * Stock on hand should never be negative. This can occur when stock has been issued
   * in a customer invoice for this batch before the parent stocktake was finalised.
   *
   * @return  {boolean}
   */
  get isReducedBelowMinimum() {
    const { totalQuantity: stockOnHand } = this.itemBatch;

    // Return true if |stockOnHand + stocktakebatch.difference| is negative.
    return stockOnHand + this.difference < 0;
  }

  /**
   * Get if snapshot is out-of-date.
   *
   * Returns true if the |snapshotTotalQuantity| does not match the |totalQuantity| of the
   * item batch this stocktake batch is associated with. Will occur if inventory has changed
   * since this stocktake batch was created. If the net change on this batch is 0, then return
   * false.
   *
   * @return  {boolean}
   */
  get isSnapshotOutdated() {
    return this.snapshotTotalQuantity !== this.itemBatch.totalQuantity;
  }

  /**
   * Get if stocktake batch has been counted.
   *
   * @return  {boolean}
   */
  get hasBeenCounted() {
    return this.countedNumberOfPacks !== null;
  }

  /**
   * Get id of item associated with this stocktake batch.
   *
   * @return  {string}
   */
  get itemId() {
    if (!this.itemBatch) return '';
    return this.itemBatch.item ? this.itemBatch.item.id : '';
  }

  /**
   * Get id of item batch associated with this stocktake batch.
   *
   * @return  {string}
   */
  get itemBatchId() {
    return this.itemBatch ? this.itemBatch.id : '';
  }

  /**
   * Get difference between counted quantity and snapshop quantity.
   *
   * @return  {number}
   */
  get difference() {
    return this.countedTotalQuantity - this.snapshotTotalQuantity;
  }

  /**
   * Indicator that this batch has a positive difference in snapshot and
   * counted quantity.
   */
  get hasPositiveAdjustment() {
    return this.difference > 0;
  }

  /**
   * Indicator that this batch has a negative difference in snapshot and
   * counted quantity.
   */
  get hasNegativeAdjustment() {
    return this.difference < 0;
  }

  /**
   * Returns a string representing the units for this stocktake batch.
   * @return {string} the unit for this stocktake batch, or N/A if none has been assigned.
   */
  get unitString() {
    return this.itemBatch?.unitString;
  }

  /**
   * Returns an indicator that this batches reason/option state is valid.
   * Valid being: negative differences require a negativeInventoryAdjustment
   * option. Positive differences require a positiveInventoryAdjustment option
   * while no difference requires there to be no option applied.
   */
  get hasValidReason() {
    // Short circuits for simple cases
    if (!this.difference && !this.option) return true;
    if (this.difference && !this.option) return false;

    // Determine the validity of the reason state where there is a difference
    // and the batch already has a option.
    const { type } = this.option;

    const positiveAdjustmentReason = type === 'positiveInventoryAdjustment';
    const negativeAdjustmentReason = type === 'negativeInventoryAdjustment';

    const correctPositiveReason = positiveAdjustmentReason && this.hasPositiveAdjustment;
    const correctNegativeReason = negativeAdjustmentReason && this.hasNegativeAdjustment;

    return correctNegativeReason || correctPositiveReason;
  }

  /**
   * @return {String} this batches reason title, or an empty string.
   */
  get reasonTitle() {
    return (this.option && this.option.title) || '';
  }

  /**
   * Set counted total quantity.
   *
   * @param  {number}  quantity
   */
  set countedTotalQuantity(quantity) {
    // Handle packsize of 0.
    const countedNumberOfPacks = this.packSize ? quantity / this.packSize : 0;

    this.countedNumberOfPacks = countedNumberOfPacks;
    this.doses = countedNumberOfPacks * this.dosesPerVial;
  }

  get isVaccine() {
    return this.itemBatch?.isVaccine ?? false;
  }

  get dosesPerVial() {
    return this.isVaccine ? this.itemBatch?.item?.doses ?? 0 : 0;
  }

  setDoses(database, newValue) {
    const maximumDosesPossible = this.dosesPerVial * this.countedTotalQuantity;
    this.doses = Math.min(newValue, maximumDosesPossible);

    database.save('StocktakeBatch', this);
  }

  /**
   * Removes a reason from this batch if it already has a reason and difference.
   * @param {Realm} database App-wide database interface.
   */
  removeReason(database) {
    database.write(() => {
      this.option = null;
      database.save('StocktakeBatch', this);
    });
  }

  /**
   * Applies a reason to this batch after it has been validated as a valid reason
   *
   * @param {Realm} database App-wide database interface
   * @param {Option} newOption New option to apply
   */
  applyReason(database, newOption) {
    const { type: newOptionType } = newOption || {};

    const isPositiveAdjustmentReason = newOptionType === 'positiveInventoryAdjustment';
    const isNegativeAdjustmentReason = newOptionType === 'negativeInventoryAdjustment';

    // Valid adjustments are when this batch has a difference in snapshot quantity and
    // counted quantity and if the difference is positive, the reason must be a positive
    // reason also (and vice-versa for negatives)
    const isValidPositiveAdjustment =
      !!this.difference && this.hasPositiveAdjustment && isPositiveAdjustmentReason;
    const isValidNegativeAdjustment =
      !!this.difference && this.hasNegativeAdjustment && isNegativeAdjustmentReason;

    database.write(() => {
      database.update('StocktakeBatch', {
        ...this,
        option: isValidPositiveAdjustment || isValidNegativeAdjustment ? newOption : null,
      });
    });
  }

  /**
   * Finalise stocktake batch.
   *
   * Finalising will adjust inventory appropriately and will add a new transaction
   * batch in reducing or increasing transaction for this stocktake.
   *
   * @param  {Realm}  database
   */
  finalise(database) {
    // Update the item batch details.
    this.itemBatch.batch = this.batch;
    this.itemBatch.expiryDate = this.expiryDate;
    this.itemBatch.sellPrice = this.sellPrice;
    this.itemBatch.supplier = this.supplier;

    if (this.itemBatch.shouldApplyVvmStatus(this.vaccineVialMonitorStatus)) {
      this.itemBatch.applyVvmStatus(database, this.vaccineVialMonitorStatus);
    }

    if (this.itemBatch.shouldApplyLocation(this.location)) {
      this.itemBatch.applyLocation(database, this.location);
    }

    // Make inventory adjustments if there is a difference to apply.
    if (this.difference !== 0) {
      const isAddition = this.countedTotalQuantity > this.snapshotTotalQuantity;
      const inventoryAdjustment = isAddition
        ? this.stocktake.getAdditions(database)
        : this.stocktake.getReductions(database);

      // Create transaction item and transaction batch to store inventory adjustments in this
      // stocktake.
      const transactionItem = createRecord(
        database,
        'TransactionItem',
        inventoryAdjustment,
        this.itemBatch.item
      );
      const transactionBatch = createRecord(
        database,
        'TransactionBatch',
        transactionItem,
        this.itemBatch,
        isAddition
      );

      // Apply difference from stocktake to actual stock on hand levels. Whether stock is increased
      // or decreased is determined by the transaction, use the absolute value of difference
      // (i.e. always treat as positive).
      const snapshotDifference = Math.abs(this.difference);
      transactionBatch.setTotalQuantity(database, snapshotDifference);
      transactionBatch.doses = this.doses;

      database.save('TransactionBatch', transactionBatch);
    }
    database.save('ItemBatch', this.itemBatch);
  }

  /**
   * Get string representation of stocktake batch.
   *
   * @return  {string}
   */
  toString() {
    return `Stocktake batch representing ${this.itemBatch}`;
  }

  get breaches() {
    return this.itemBatch?.breaches;
  }

  get itemName() {
    return this?.itemBatch?.itemName;
  }

  get costPriceString() {
    return currency(this.costPrice ?? 0, { formatWithSymbol: true }).format();
  }

  get sellPriceString() {
    return currency(this.sellPrice ?? 0, { formatWithSymbol: true }).format();
  }
}

StocktakeBatch.schema = {
  name: 'StocktakeBatch',
  primaryKey: 'id',
  properties: {
    id: 'string',
    stocktake: 'Stocktake',
    itemBatch: 'ItemBatch',
    snapshotNumberOfPacks: 'double',
    packSize: 'double',
    expiryDate: { type: 'date', optional: true },
    batch: 'string',
    costPrice: 'double',
    sellPrice: 'double',
    countedNumberOfPacks: { type: 'double', optional: true },
    sortIndex: { type: 'int', optional: true },
    option: { type: 'Options', optional: true },
    supplier: { type: 'Name', optional: true },
    location: { type: 'Location', optional: true },
    doses: { type: 'double', default: 0 },
    vaccineVialMonitorStatus: { type: 'VaccineVialMonitorStatus', optional: true },
  },
};

export default StocktakeBatch;
<commit_msg>Add logic to leave locations when finalising a stocktake<commit_after>import Realm from 'realm';

import currency from '../../localization/currency';
import { createRecord } from '../utilities';

/**
 * A stocktake batch.
 *
 * @property  {string}     id
 * @property  {Stocktake}  stocktake
 * @property  {ItemBatch}  itemBatch
 * @property  {number}     snapshotNumberOfPacks
 * @property  {number}     packSize
 * @property  {Date}       expiryDate
 * @property  {string}     batch
 * @property  {number}     costPrice
 * @property  {number}     sellPrice
 * @property  {number}     countedNumberOfPacks
 * @property  {number}     sortIndex
 */
export class StocktakeBatch extends Realm.Object {
  /**
   * Delete stocktake batch and associated item batch.
   *
   * @param  {Realm}  database
   */
  destructor(database) {
    if (this.snapshotNumberOfPacks === 0 && this.itemBatch.numberOfPacks === 0) {
      database.delete('ItemBatch', this.itemBatch);
    }
  }

  get currentLocationName() {
    return this.location?.description ?? '';
  }

  get currentVvmStatusName() {
    return this.vaccineVialMonitorStatus?.description ?? '';
  }

  get hasBreached() {
    return !!this.itemBatch?.hasBreached;
  }

  get otherPartyName() {
    return this.supplier?.name ?? '';
  }

  /**
   * @return {Bool} Indicator if this batch has a valid number of doses.
   */
  get hasValidDoses() {
    const { item } = this.itemBatch;
    const { doses: dosesPerVial } = item;

    const maximumDosesPossible = this.snapshotTotalQuantity * dosesPerVial;
    const minimumDosesPossible = this.snapshotTotalQuantity;

    const tooManyDoses = this.doses > maximumDosesPossible;
    const tooLittleDoses = this.doses < minimumDosesPossible;
    const justRightDoses = !tooManyDoses && !tooLittleDoses;

    return justRightDoses;
  }

  /**
   * Get snapshot of total quantity in batch.
   *
   * @return  {number}
   */
  get snapshotTotalQuantity() {
    return this.snapshotNumberOfPacks * this.packSize;
  }

  /**
   * Get the total countedQuantity for this batch. If nothing has been
   * counted, return 0.
   *
   * @return  {number}
   */
  get countedTotalQuantity() {
    if (this.countedNumberOfPacks === null) return 0;
    return this.countedNumberOfPacks * this.packSize;
  }

  /**
   * Get if stocktake will reduce stock on hand of item below zero.
   *
   * Stock on hand should never be negative. This can occur when stock has been issued
   * in a customer invoice for this batch before the parent stocktake was finalised.
   *
   * @return  {boolean}
   */
  get isReducedBelowMinimum() {
    const { totalQuantity: stockOnHand } = this.itemBatch;

    // Return true if |stockOnHand + stocktakebatch.difference| is negative.
    return stockOnHand + this.difference < 0;
  }

  /**
   * Get if snapshot is out-of-date.
   *
   * Returns true if the |snapshotTotalQuantity| does not match the |totalQuantity| of the
   * item batch this stocktake batch is associated with. Will occur if inventory has changed
   * since this stocktake batch was created. If the net change on this batch is 0, then return
   * false.
   *
   * @return  {boolean}
   */
  get isSnapshotOutdated() {
    return this.snapshotTotalQuantity !== this.itemBatch.totalQuantity;
  }

  /**
   * Get if stocktake batch has been counted.
   *
   * @return  {boolean}
   */
  get hasBeenCounted() {
    return this.countedNumberOfPacks !== null;
  }

  /**
   * Get id of item associated with this stocktake batch.
   *
   * @return  {string}
   */
  get itemId() {
    if (!this.itemBatch) return '';
    return this.itemBatch.item ? this.itemBatch.item.id : '';
  }

  /**
   * Get id of item batch associated with this stocktake batch.
   *
   * @return  {string}
   */
  get itemBatchId() {
    return this.itemBatch ? this.itemBatch.id : '';
  }

  /**
   * Get difference between counted quantity and snapshop quantity.
   *
   * @return  {number}
   */
  get difference() {
    return this.countedTotalQuantity - this.snapshotTotalQuantity;
  }

  /**
   * Indicator that this batch has a positive difference in snapshot and
   * counted quantity.
   */
  get hasPositiveAdjustment() {
    return this.difference > 0;
  }

  /**
   * Indicator that this batch has a negative difference in snapshot and
   * counted quantity.
   */
  get hasNegativeAdjustment() {
    return this.difference < 0;
  }

  /**
   * Returns a string representing the units for this stocktake batch.
   * @return {string} the unit for this stocktake batch, or N/A if none has been assigned.
   */
  get unitString() {
    return this.itemBatch?.unitString;
  }

  /**
   * Returns an indicator that this batches reason/option state is valid.
   * Valid being: negative differences require a negativeInventoryAdjustment
   * option. Positive differences require a positiveInventoryAdjustment option
   * while no difference requires there to be no option applied.
   */
  get hasValidReason() {
    // Short circuits for simple cases
    if (!this.difference && !this.option) return true;
    if (this.difference && !this.option) return false;

    // Determine the validity of the reason state where there is a difference
    // and the batch already has a option.
    const { type } = this.option;

    const positiveAdjustmentReason = type === 'positiveInventoryAdjustment';
    const negativeAdjustmentReason = type === 'negativeInventoryAdjustment';

    const correctPositiveReason = positiveAdjustmentReason && this.hasPositiveAdjustment;
    const correctNegativeReason = negativeAdjustmentReason && this.hasNegativeAdjustment;

    return correctNegativeReason || correctPositiveReason;
  }

  /**
   * @return {String} this batches reason title, or an empty string.
   */
  get reasonTitle() {
    return (this.option && this.option.title) || '';
  }

  /**
   * Set counted total quantity.
   *
   * @param  {number}  quantity
   */
  set countedTotalQuantity(quantity) {
    // Handle packsize of 0.
    const countedNumberOfPacks = this.packSize ? quantity / this.packSize : 0;

    this.countedNumberOfPacks = countedNumberOfPacks;
    this.doses = countedNumberOfPacks * this.dosesPerVial;
  }

  get isVaccine() {
    return this.itemBatch?.isVaccine ?? false;
  }

  get dosesPerVial() {
    return this.isVaccine ? this.itemBatch?.item?.doses ?? 0 : 0;
  }

  setDoses(database, newValue) {
    const maximumDosesPossible = this.dosesPerVial * this.countedTotalQuantity;
    this.doses = Math.min(newValue, maximumDosesPossible);

    database.save('StocktakeBatch', this);
  }

  /**
   * Removes a reason from this batch if it already has a reason and difference.
   * @param {Realm} database App-wide database interface.
   */
  removeReason(database) {
    database.write(() => {
      this.option = null;
      database.save('StocktakeBatch', this);
    });
  }

  /**
   * Applies a reason to this batch after it has been validated as a valid reason
   *
   * @param {Realm} database App-wide database interface
   * @param {Option} newOption New option to apply
   */
  applyReason(database, newOption) {
    const { type: newOptionType } = newOption || {};

    const isPositiveAdjustmentReason = newOptionType === 'positiveInventoryAdjustment';
    const isNegativeAdjustmentReason = newOptionType === 'negativeInventoryAdjustment';

    // Valid adjustments are when this batch has a difference in snapshot quantity and
    // counted quantity and if the difference is positive, the reason must be a positive
    // reason also (and vice-versa for negatives)
    const isValidPositiveAdjustment =
      !!this.difference && this.hasPositiveAdjustment && isPositiveAdjustmentReason;
    const isValidNegativeAdjustment =
      !!this.difference && this.hasNegativeAdjustment && isNegativeAdjustmentReason;

    database.write(() => {
      database.update('StocktakeBatch', {
        ...this,
        option: isValidPositiveAdjustment || isValidNegativeAdjustment ? newOption : null,
      });
    });
  }

  /**
   * Finalise stocktake batch.
   *
   * Finalising will adjust inventory appropriately and will add a new transaction
   * batch in reducing or increasing transaction for this stocktake.
   *
   * @param  {Realm}  database
   */
  finalise(database) {
    // Update the item batch details.
    this.itemBatch.batch = this.batch;
    this.itemBatch.expiryDate = this.expiryDate;
    this.itemBatch.sellPrice = this.sellPrice;
    this.itemBatch.supplier = this.supplier;

    if (this.itemBatch.shouldApplyVvmStatus(this.vaccineVialMonitorStatus)) {
      this.itemBatch.applyVvmStatus(database, this.vaccineVialMonitorStatus);
    }

    if (this.itemBatch.shouldApplyLocation(this.location)) {
      this.itemBatch.applyLocation(database, this.location);
    }

    // Make inventory adjustments if there is a difference to apply.
    if (this.difference !== 0) {
      const isAddition = this.countedTotalQuantity > this.snapshotTotalQuantity;
      const inventoryAdjustment = isAddition
        ? this.stocktake.getAdditions(database)
        : this.stocktake.getReductions(database);

      // Create transaction item and transaction batch to store inventory adjustments in this
      // stocktake.
      const transactionItem = createRecord(
        database,
        'TransactionItem',
        inventoryAdjustment,
        this.itemBatch.item
      );
      const transactionBatch = createRecord(
        database,
        'TransactionBatch',
        transactionItem,
        this.itemBatch,
        isAddition
      );

      // Apply difference from stocktake to actual stock on hand levels. Whether stock is increased
      // or decreased is determined by the transaction, use the absolute value of difference
      // (i.e. always treat as positive).
      const snapshotDifference = Math.abs(this.difference);
      transactionBatch.setTotalQuantity(database, snapshotDifference);
      transactionBatch.doses = this.doses;
      database.save('TransactionBatch', transactionBatch);

      if (!this.itemBatch.totalQuantity) this.itemBatch.leaveLocation(database);
    }
    database.save('ItemBatch', this.itemBatch);
  }

  /**
   * Get string representation of stocktake batch.
   *
   * @return  {string}
   */
  toString() {
    return `Stocktake batch representing ${this.itemBatch}`;
  }

  get breaches() {
    return this.itemBatch?.breaches;
  }

  get itemName() {
    return this?.itemBatch?.itemName;
  }

  get costPriceString() {
    return currency(this.costPrice ?? 0, { formatWithSymbol: true }).format();
  }

  get sellPriceString() {
    return currency(this.sellPrice ?? 0, { formatWithSymbol: true }).format();
  }
}

StocktakeBatch.schema = {
  name: 'StocktakeBatch',
  primaryKey: 'id',
  properties: {
    id: 'string',
    stocktake: 'Stocktake',
    itemBatch: 'ItemBatch',
    snapshotNumberOfPacks: 'double',
    packSize: 'double',
    expiryDate: { type: 'date', optional: true },
    batch: 'string',
    costPrice: 'double',
    sellPrice: 'double',
    countedNumberOfPacks: { type: 'double', optional: true },
    sortIndex: { type: 'int', optional: true },
    option: { type: 'Options', optional: true },
    supplier: { type: 'Name', optional: true },
    location: { type: 'Location', optional: true },
    doses: { type: 'double', default: 0 },
    vaccineVialMonitorStatus: { type: 'VaccineVialMonitorStatus', optional: true },
  },
};

export default StocktakeBatch;
<|endoftext|>


<commit_before>/* global Buffer */

var http  = require('http'),
    https = require('https'),
    Url   = require('url'),
    EventEmitter = require('events').EventEmitter,
    querystring = require('querystring');

var MAX_DOCS = 1000; // The maximum number of docs to send in a single batch
var noOp = function(err) { if (err) { throw err; } }

var CONNECTION_DEFAULTS = {
  host: '127.0.0.1:5984',
  port: 5984,
  hostname: '127.0.0.1',
  pathname: "/"
};

function CouchClient(url) {
  var uri = Url.parse(url);
  uri.protocolHandler = uri.protocol == 'http:' ? http : https;
  uri.__proto__ = CONNECTION_DEFAULTS;
  var revCache = {};

  // A simple wrapper around node's http(s) request.
  function request(method, path, body, callback) {
    var stream;
    // Body is optional
    if (typeof body === 'function' && typeof callback === 'undefined') {
      callback = body;
      body = undefined;
    }

    // Return a stream if no callback is specified
    if (!callback) {
      stream = new EventEmitter();
      stream.setEncoding = function () {
        throw new Error("This stream is always utf8");
      };
    }

    function errorHandler(err) {
      if (callback) { callback(err); }
      if (stream) { stream.emit('error', err); }
    }

    var headers = {
      "Host": uri.hostname
    };

    if (uri.auth) {
      headers["Authorization"] = "Basic " + new Buffer(uri.auth, "ascii").toString("base64");
    }

    if (body) {
      body = JSON.stringify(body);
      headers["Content-Length"] = Buffer.byteLength(body);
      headers["Content-Type"] = "application/json";
    }

    var options = {
      host: uri.hostname,
      method: method,
      path: path,
      port: uri.port,
      headers: headers
    };

    var request = uri.protocolHandler.request(options, function (response) {
      response.setEncoding('utf8');
      var body = "";
      response.on('data', function (chunk) {
        if (callback) { body += chunk; }
        if (stream) { stream.emit('data', chunk); }
      });
      response.on('end', function () {
        if (callback) { callback(null, JSON.parse(body)); }
        if (stream) { stream.emit('end'); }
      });
      response.on('error', errorHandler);
    });
    request.on('error', errorHandler);

    if (body) { request.write(body, 'utf8'); }
    request.end();
    return stream;
  }

  // Requests UUIDs from the couch server in tick batches
  var uuidQueue = [];
  function getUUID(callback) {
    uuidQueue.push(callback);
    if (uuidQueue.length > 1) { return; }
    function consumeQueue() {
      var pending = uuidQueue.splice(0, MAX_DOCS);
      if (uuidQueue.length) { process.nextTick(consumeQueue); }
      request("GET", "/_uuids?count=" + pending.length, function (err, result) {
        if (err) {
          pending.forEach(function (callback) {
            callback(err);
          });
          return;
        }
        if (result.uuids.length !== pending.length) {
          throw new Error("Wrong number of UUIDs generated " + result.uuids.length + " != " + pending.length);
        }
        result.uuids.forEach(function (uuid, i) {
          pending[i](null, uuid);
        });
      });
    }
    process.nextTick(consumeQueue);
  }

  // Saves documents in batches
  var saveValues = [];
  var saveQueue = [];
  function realSave(doc, callback) {
    // Put key and rev on the value without changing the original
    saveValues.push(doc);
    saveQueue.push(callback);
    if (saveQueue.length > 1) { return; }

    function consumeQueue() {
      var pending = saveQueue.splice(0, MAX_DOCS);
      var body = saveValues.splice(0, MAX_DOCS);
      if (saveQueue.length) { process.nextTick(consumeQueue); }
      request("POST", uri.pathname + "/_bulk_docs", {docs: body}, function (err, results) {
        if (!err && results && results.error) err = results;
        if (err) {
          pending.forEach(function (callback) {
            callback(err);
          });
          return;
        }
        results.forEach(function (result, i) {
          var doc = body[i];
          if (result.error && result.error === "conflict") {
            // handle update conflict
            pending[i](result);
          } else {
            doc._id = result.id;
            doc._rev = result.rev;
            pending[i](null, doc);
          }
        });
      });
    }
    process.nextTick(consumeQueue);
  }

  var getQueue = [];
  var getKeys = [];
  function realGet(key, includeDoc, callback) {
    getKeys.push(key);
    getQueue.push(callback);
    if (getQueue.length > 1) { return; }
    function consumeQueue() {
      var pending = getQueue.splice(0, MAX_DOCS);
      var keys = getKeys.splice(0, MAX_DOCS);
      if (getQueue.length) { process.nextTick(consumeQueue); }
      var path = uri.pathname + "/_all_docs";
      if (includeDoc) { path += "?include_docs=true"; }
      request("POST", path, {keys: keys}, function (err, results) {
        if (!results.rows) err = results;
        if (err) {
          pending.forEach(function (callback) {
            callback(err);
          });
          return;
        }
        results.rows.forEach(function (result, i) {
          var err;
          if (includeDoc) {
            if (result.error) {
              err = result;
              pending[i](err);
              return;
            }
            pending[i](null, result.doc);
            return;
          }
          pending[i](null, result.value);
        });
      });
    }
    process.nextTick(consumeQueue);
  }

  function save(doc, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = null;
    }
    if (!callback) { callback = noOp; }
    if (doc._id) {
      if (!doc._rev && options && options.force) {
        if (!revCache.hasOwnProperty(doc._id)) {
          realGet(doc._id, false, function (err, result) {
            if (err) { return callback(err); }
            if (result) {
              revCache[doc._id] = result.rev;
              doc._rev = result.rev;
            }
            realSave(doc, callback);
          });
          return;
        }
        doc._rev = revCache[doc._id];
      }
    }
    realSave(doc, callback);
  }

  function get(key, callback) {
    realGet(key, true, callback);
  }

  function changes(since, callback) {
    var stream = request("GET", uri.pathname + "/_changes?feed=continuous&heartbeat=1000&since=" + since);
    var data = "";
    function checkData() {
      var p = data.indexOf("\n");
      if (p >= 0) {
        var line = data.substr(0, p).trim();
        data = data.substr(p + 1);
        if (line.length) {
          callback(null, JSON.parse(line));
        }
        checkData();
      }
    }

    stream.on('error', callback);
    stream.on('data', function (chunk) {
      data += chunk;
      checkData();
    });
    stream.on('end', function () {
      throw new Error("Changes feed got broken!");
    });
  }

  function view(path, obj, callback) {
  	var method = "GET";
  	var body = null;

    if (typeof obj === 'function') {
      callback = obj;
      obj = null;
    }

    path = path.split('/');
    path = [uri.pathname, '_design', path[0], '_view', path[1]].join('/');

    if (obj && typeof obj === 'object') {
      Object.keys(obj).forEach(function(key){
		    if ( key === 'keys' ) {
			    body = { keys: obj[key] }; // body is json stringified in request fn
			    method='POST';
		    } else {
			    obj[key] = JSON.stringify(obj[key]);
		    }
      });

      var getParams = querystring.stringify(obj);
      if (getParams) {
        path = path + '?' + getParams;
      }
    }
    request(method, path, body, function(err, result) {
      result && result.error ? callback(result) : callback(err, result);
    });
  }

  // Expose the public API
  return {
    get: get,
    save: save,
    changes: changes,
    request: request,
    uri: uri,
    view: view
  };
}

module.exports = CouchClient;<commit_msg>Removed revCache. Since we're running in secure mode, we're leaving that task to the client.<commit_after>/* global Buffer */

var http  = require('http'),
    https = require('https'),
    Url   = require('url'),
    EventEmitter = require('events').EventEmitter,
    querystring = require('querystring');

var MAX_DOCS = 1000; // The maximum number of docs to send in a single batch
var noOp = function(err) { if (err) { throw err; } }

var CONNECTION_DEFAULTS = {
  host: '127.0.0.1:5984',
  port: 5984,
  hostname: '127.0.0.1',
  pathname: "/"
};

function CouchClient(url) {
  var uri = Url.parse(url);
  uri.protocolHandler = uri.protocol == 'http:' ? http : https;
  uri.__proto__ = CONNECTION_DEFAULTS;

  // A simple wrapper around node's http(s) request.
  function request(method, path, body, callback) {
    var stream;
    // Body is optional
    if (typeof body === 'function' && typeof callback === 'undefined') {
      callback = body;
      body = undefined;
    }

    // Return a stream if no callback is specified
    if (!callback) {
      stream = new EventEmitter();
      stream.setEncoding = function () {
        throw new Error("This stream is always utf8");
      };
    }

    function errorHandler(err) {
      if (callback) { callback(err); }
      if (stream) { stream.emit('error', err); }
    }

    var headers = {
      "Host": uri.hostname
    };

    if (uri.auth) {
      headers["Authorization"] = "Basic " + new Buffer(uri.auth, "ascii").toString("base64");
    }

    if (body) {
      body = JSON.stringify(body);
      headers["Content-Length"] = Buffer.byteLength(body);
      headers["Content-Type"] = "application/json";
    }

    var options = {
      host: uri.hostname,
      method: method,
      path: path,
      port: uri.port,
      headers: headers
    };

    var request = uri.protocolHandler.request(options, function (response) {
      response.setEncoding('utf8');
      var body = "";
      response.on('data', function (chunk) {
        if (callback) { body += chunk; }
        if (stream) { stream.emit('data', chunk); }
      });
      response.on('end', function () {
        if (callback) { callback(null, JSON.parse(body)); }
        if (stream) { stream.emit('end'); }
      });
      response.on('error', errorHandler);
    });
    request.on('error', errorHandler);

    if (body) { request.write(body, 'utf8'); }
    request.end();
    return stream;
  }

  // Requests UUIDs from the couch server in tick batches
  var uuidQueue = [];
  function getUUID(callback) {
    uuidQueue.push(callback);
    if (uuidQueue.length > 1) { return; }
    function consumeQueue() {
      var pending = uuidQueue.splice(0, MAX_DOCS);
      if (uuidQueue.length) { process.nextTick(consumeQueue); }
      request("GET", "/_uuids?count=" + pending.length, function (err, result) {
        if (err) {
          pending.forEach(function (callback) {
            callback(err);
          });
          return;
        }
        if (result.uuids.length !== pending.length) {
          throw new Error("Wrong number of UUIDs generated " + result.uuids.length + " != " + pending.length);
        }
        result.uuids.forEach(function (uuid, i) {
          pending[i](null, uuid);
        });
      });
    }
    process.nextTick(consumeQueue);
  }

  // Saves documents in batches
  var saveValues = [];
  var saveQueue = [];
  function realSave(doc, callback) {
    // Put key and rev on the value without changing the original
    saveValues.push(doc);
    saveQueue.push(callback);
    if (saveQueue.length > 1) { return; }

    function consumeQueue() {
      var pending = saveQueue.splice(0, MAX_DOCS);
      var body = saveValues.splice(0, MAX_DOCS);
      if (saveQueue.length) { process.nextTick(consumeQueue); }
      request("POST", uri.pathname + "/_bulk_docs", {docs: body}, function (err, results) {
        if (!err && results && results.error) err = results;
        if (err) {
          pending.forEach(function (callback) {
            callback(err);
          });
          return;
        }
        results.forEach(function (result, i) {
          var doc = body[i];
          if (result.error && result.error === "conflict") {
            // handle update conflict
            pending[i](result);
          } else {
            doc._id = result.id;
            doc._rev = result.rev;
            pending[i](null, doc);
          }
        });
      });
    }
    process.nextTick(consumeQueue);
  }

  var getQueue = [];
  var getKeys = [];
  function realGet(key, includeDoc, callback) {
    getKeys.push(key);
    getQueue.push(callback);
    if (getQueue.length > 1) { return; }
    function consumeQueue() {
      var pending = getQueue.splice(0, MAX_DOCS);
      var keys = getKeys.splice(0, MAX_DOCS);
      if (getQueue.length) { process.nextTick(consumeQueue); }
      var path = uri.pathname + "/_all_docs";
      if (includeDoc) { path += "?include_docs=true"; }
      request("POST", path, {keys: keys}, function (err, results) {
        if (!results.rows) err = results;
        if (err) {
          pending.forEach(function (callback) {
            callback(err);
          });
          return;
        }
        results.rows.forEach(function (result, i) {
          var err;
          if (includeDoc) {
            if (result.error) {
              err = result;
              pending[i](err);
              return;
            }
            pending[i](null, result.doc);
            return;
          }
          pending[i](null, result.value);
        });
      });
    }
    process.nextTick(consumeQueue);
  }

  function save(doc, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = null;
    }
    if (!callback) { callback = noOp; }
    if (doc._id) {
      if (!doc._rev && options && options.force) {
        realGet(doc._id, false, function (err, result) {
          if (err) { return callback(err); }
          if (result) doc._rev = result.rev;
          realSave(doc, callback);
        });
        return;
      }
    }
    realSave(doc, callback);
  }

  function get(key, callback) {
    realGet(key, true, callback);
  }

  function changes(since, callback) {
    var stream = request("GET", uri.pathname + "/_changes?feed=continuous&heartbeat=1000&since=" + since);
    var data = "";
    function checkData() {
      var p = data.indexOf("\n");
      if (p >= 0) {
        var line = data.substr(0, p).trim();
        data = data.substr(p + 1);
        if (line.length) {
          callback(null, JSON.parse(line));
        }
        checkData();
      }
    }

    stream.on('error', callback);
    stream.on('data', function (chunk) {
      data += chunk;
      checkData();
    });
    stream.on('end', function () {
      throw new Error("Changes feed got broken!");
    });
  }

  function view(path, obj, callback) {
  	var method = "GET";
  	var body = null;

    if (typeof obj === 'function') {
      callback = obj;
      obj = null;
    }

    path = path.split('/');
    path = [uri.pathname, '_design', path[0], '_view', path[1]].join('/');

    if (obj && typeof obj === 'object') {
      Object.keys(obj).forEach(function(key){
		    if ( key === 'keys' ) {
			    body = { keys: obj[key] }; // body is json stringified in request fn
			    method='POST';
		    } else {
			    obj[key] = JSON.stringify(obj[key]);
		    }
      });

      var getParams = querystring.stringify(obj);
      if (getParams) {
        path = path + '?' + getParams;
      }
    }
    request(method, path, body, function(err, result) {
      result && result.error ? callback(result) : callback(err, result);
    });
  }

  // Expose the public API
  return {
    get: get,
    save: save,
    changes: changes,
    request: request,
    uri: uri,
    view: view
  };
}

module.exports = CouchClient;<|endoftext|>


<commit_before>/**
 * @file Radio component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import IconButton from '../IconButton';
import Theme from '../Theme';
import TipProvider from '../TipProvider';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';

class Radio extends Component {

    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            checked: !!props.checked
        };

        this.touchTapHandler = ::this.touchTapHandler;
        this.mouseDownHandler = ::this.mouseDownHandler;
        this.mouseUpHandler = ::this.mouseUpHandler;

    }

    touchTapHandler() {

        let {checked} = this.state;

        if (checked) {
            return;
        }

        this.setState({
            checked: true
        }, () => {

            const {onChange, onCheck} = this.props;

            onChange && onChange(true);
            onCheck && onCheck();

        });

    }

    mouseDownHandler(e) {

        if (this.props.disabled) {
            return;
        }

        this.refs.radioIcon.startRipple(e);
        this.touchTapHandler();

    }

    mouseUpHandler(e) {

        if (this.props.disabled) {
            return;
        }

        this.refs.radioIcon.endRipple(e);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.checked !== this.state.checked) {
            this.setState({
                checked: !!nextProps.checked
            });
        }
    }

    render() {

        const {
                className, style, theme, name, label, value, uncheckedIconCls, checkedIconCls, disabled,
                disableTouchRipple, tip, tipPosition
            } = this.props,
            {checked} = this.state,

            radioClassName = classNames('radio', {
                activated: checked,
                [`theme-${theme}`]: theme,
                [className]: className
            });

        return (
            <TipProvider text={tip}
                         position={tipPosition}>

                    {
                        name ?
                            <input className="hidden-radio"
                                   type="radio"
                                   name={name}
                                   value={value}
                                   checked={checked}
                                   onChange={() => {
                                   }}/>
                            :
                            null
                    }

                        <IconButton ref="radioIcon"
                                    className="radio-bg-icon"
                                    iconCls={uncheckedIconCls}
                                    onTouchTap={this.touchTapHandler}
                                    disabled={disabled}
                                    disableTouchRipple={disableTouchRipple}/>
                        <IconButton className="radio-icon"
                                    iconCls={checkedIconCls}
                                    onTouchTap={this.touchTapHandler}
                                    disabled={disabled}
                                    disableTouchRipple={disableTouchRipple}/>

                    {
                        label ?

                                {label}

                            :
                            null
                    }

            </TipProvider>
        );

    }
};

Radio.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The Radio theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The name of the radio.
     */
    name: PropTypes.string,

    /**
     * Label for radio.
     */
    label: PropTypes.any,

    /**
     * If true,the radio will be checked.
     */
    checked: PropTypes.bool,

    uncheckedIconCls: PropTypes.string,
    checkedIconCls: PropTypes.string,

    /**
     * If true, the radio will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the element's ripple effect will be disabled.
     */
    disableTouchRipple: PropTypes.bool,

    tip: PropTypes.any,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * Callback function fired when the radio status change.
     */
    onChange: PropTypes.func,

    onCheck: PropTypes.func

};

Radio.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    name: null,
    label: null,
    checked: false,
    uncheckedIconCls: 'far fa-circle',
    checkedIconCls: 'fas fa-dot-circle',
    disabled: false,
    disableTouchRipple: false,

    tip: null,
    tipPosition: Position.BOTTOM

};

export default Radio;<commit_msg>Radio bug fixed<commit_after>/**
 * @file Radio component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import IconButton from '../IconButton';
import Theme from '../Theme';
import TipProvider from '../TipProvider';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';

class Radio extends Component {

    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            checked: !!props.checked
        };

        this.touchTapHandler = ::this.touchTapHandler;
        this.mouseDownHandler = ::this.mouseDownHandler;
        this.mouseUpHandler = ::this.mouseUpHandler;

    }

    touchTapHandler() {

        let {checked} = this.state;

        if (checked) {
            return;
        }

        this.setState({
            checked: true
        }, () => {

            const {onChange, onCheck} = this.props;

            onChange && onChange(true);
            onCheck && onCheck();

        });

    }

    mouseDownHandler(e) {

        if (this.props.disabled) {
            return;
        }

        this.refs.radioIcon.startRipple(e);
        this.touchTapHandler();

    }

    mouseUpHandler(e) {

        if (this.props.disabled) {
            return;
        }

        this.refs.radioIcon.endRipple(e);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.checked !== this.state.checked) {
            this.setState({
                checked: !!nextProps.checked
            });
        }
    }

    render() {

        const {
                className, style, theme, name, label, value, uncheckedIconCls, checkedIconCls, disabled,
                disableTouchRipple, tip, tipPosition
            } = this.props,
            {checked} = this.state,

            radioClassName = classNames('radio', {
                activated: checked,
                [`theme-${theme}`]: theme,
                [className]: className
            });

        return (
            <TipProvider text={tip}
                         position={tipPosition}>

                    {
                        name ?
                            <input className="hidden-radio"
                                   type="radio"
                                   name={name}
                                   value={value}
                                   checked={checked}
                                   onChange={() => {
                                   }}/>
                            :
                            null
                    }

                        <IconButton ref="radioIcon"
                                    className="radio-bg-icon"
                                    iconCls={uncheckedIconCls}
                                    onTouchTap={this.touchTapHandler}
                                    disabled={disabled}
                                    disableTouchRipple={disableTouchRipple}/>
                        <IconButton className="radio-icon"
                                    iconCls={checkedIconCls}
                                    onTouchTap={this.touchTapHandler}
                                    disabled={disabled}
                                    disableTouchRipple={disableTouchRipple}/>

                    {
                        label ?

                                {label}

                            :
                            null
                    }

            </TipProvider>
        );

    }
};

Radio.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The Radio theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The name of the radio.
     */
    name: PropTypes.string,

    /**
     * Label for radio.
     */
    label: PropTypes.any,

    /**
     * Value for radio.
     */
    value: PropTypes.any,

    /**
     * If true,the radio will be checked.
     */
    checked: PropTypes.bool,

    uncheckedIconCls: PropTypes.string,
    checkedIconCls: PropTypes.string,

    /**
     * If true, the radio will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the element's ripple effect will be disabled.
     */
    disableTouchRipple: PropTypes.bool,

    tip: PropTypes.any,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * Callback function fired when the radio status change.
     */
    onChange: PropTypes.func,

    onCheck: PropTypes.func

};

Radio.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    name: null,
    label: null,
    value: '',
    checked: false,
    uncheckedIconCls: 'far fa-circle',
    checkedIconCls: 'fas fa-dot-circle',
    disabled: false,
    disableTouchRipple: false,

    tip: null,
    tipPosition: Position.BOTTOM

};

export default Radio;<|endoftext|>


<commit_before>'use strict';
import logger from './logger';
import TradeInfo from './tradeInfo';
import account from 'binary-common-utils/account';
import Observer from 'binary-common-utils/observer';
import _Blockly from './blockly';
import storageManager from 'binary-common-utils/storageManager';
import Translator from 'translator';
import Bot from '../bot';
import Introduction from './tours/introduction';
import Welcome from './tours/welcome';
import {PlainChart as Chart} from 'binary-charts';
import _ from 'underscore';

var View = function View(){
	if ( View.instance ) {
		return View.instance;
	}
	this.observer = new Observer();
	View.instance = this;
	this.tours = {};
	this.translator = new Translator();
	this.tradeInfo = new TradeInfo();
	this.addTranslationToUi();
	this.errorAndLogHandling();
	var that = this;
	this.bot = new Bot();
	this.initPromise = new Promise(function(resolve, reject){
		that.updateTokenList();
		that.blockly = new _Blockly();
		that.blockly.initPromise.then(function(){
			that.setElementActions();
			that.initTours();
			resolve();
		});
	});
};

View.prototype = Object.create(null, {
	updateTokenList: {
		value: function updateTokenList() {
			var tokenList = storageManager.getTokenList();
			if (tokenList.length === 0) {
				$('#login').css('display', 'inline-block');
				$('#accountSelect').css('display', 'none');
				$('#logout').css('display', 'none');
			} else {
				$('#login').css('display', 'none');
				$('#accountSelect').css('display', 'inline-block');
				$('#logout').css('display', 'inline-block');
				tokenList.forEach(function (tokenInfo) {
					var str;
					if ( tokenInfo.hasOwnProperty('isVirtual') ) {
						str = (tokenInfo.isVirtual) ? 'Virtual Account' : 'Real Account';
					} else {
						str = '';
					}
					$('#accountSelect').append('<option value="' + tokenInfo.token + '">'+str + ' (' + tokenInfo.account_name+ ') ' + '</option>');
				});
			}
		}
	},
	addTranslationToUi: {
		value: function addTranslationToUi(){
			var that = this;
			$('[data-i18n-text]')
				.each(function() {
						var contents = $(this).contents();
						if (contents.length > 0) {
								if (contents.get(0).nodeType == Node.TEXT_NODE) {
										$(this).text(that.translator.translateText($(this)
								.attr('data-i18n-text')))
								.append(contents.slice(1));
								}
						} else {
						$(this)
							.text(that.translator.translateText($(this)
								.attr('data-i18n-text')));
					}
				});
		}
	},
	initTours: {
		value: function initTours() {
			this.tours.introduction = new Introduction();
			this.tours.welcome = new Welcome();
		}
	},
	startTour: {
		value: function startTour() {
			var that = this;
			$('#tours').on('change', function(e) {
					var value = $(this).val();
					if (value === '') return;
					if (that.activeTour) {
						that.activeTour.stop();
					}
					that.activeTour = that.tours[value];
					that.activeTour.start(function(){
						that.activeTour = null;
					});
			});
		}
	},
	errorAndLogHandling: {
		value: function errorAndLogHandling(){

			var that = this;

			this.observer.register('ui.error', function showError(error) {
				if (error.stack) {
					console.error(error.stack);
					if (logger.isDebug()) {
						console.log('%c' + error.stack, 'color: red');
					} else {
						logger.addLogToQueue('%c' + error.stack, 'color: red');
					}
				} else {
					console.error(error);
				}
				var message;
				if (error.message) {
					message = error.message;
				} else {
					message = error;
				}
				$.notify(message, {
					position: 'bottom right',
					className: 'error',
				});
				if (logger.isDebug()) {
					console.log('%cError: ' + message, 'color: red');
				} else {
					logger.addLogToQueue('%cError: ' + message, 'color: red');
				}
			});

			var observeForLog = function observeForLog(type, position) {
				var subtype = ( position === 'left' )? '.left' : '';
				that.observer.register('ui.log.' + type + subtype , function(message){
					if ( type === 'warn' ) {
						console.warn(message);
					}
					$.notify(message, {
						position: 'bottom ' + position,
						className: type,
					});
					if (logger.isDebug()) {
						console.log(message);
					} else {
						logger.addLogToQueue(message);
					}
				});
			};

			["success", "info", "warn", "error"].forEach(function(type){
				observeForLog(type, 'right');
				observeForLog(type, 'left');
			});

		}
	},
	setFileBrowser: {
		value: function setFileBrowser(){
			var that = this;
			var handleFileSelect = function handleFileSelect(e) {
				var files;
				if (e.type === 'drop') {
					e.stopPropagation();
					e.preventDefault();
					files = e.dataTransfer.files;
				} else {
					files = e.target.files;
				}
				files = Array.prototype.slice.apply(files);
				var file = files[0];
				if (file) {
					if (file.type.match('text/xml')) {
						readFile(file);
					} else {
						that.observer.emit('ui.log.info', that.translator.translateText('File is not supported:' + ' ') + file.name);
					}
				}
			};

			var readFile = function readFile(f) {
				var reader = new FileReader();
				reader.onload = (function (theFile) {
					$('#fileBrowser').hide();
					return function (e) {
						try {
							that.blockly.loadBlocksFile(e.target.result);
							that.observer.emit('ui.log.success', that.translator.translateText('Blocks are loaded successfully'));
						} catch (err) {
							that.observer.emit('ui.error', err);
						}
					};
				})(f);
				reader.readAsText(f);
			};

			var handleDragOver = function handleDragOver(e) {
				e.stopPropagation();
				e.preventDefault();
				e.dataTransfer.dropEffect = 'copy';
			};

			var dropZone = document.getElementById('dropZone');

			dropZone.addEventListener('dragover', handleDragOver, false);
			dropZone.addEventListener('drop', handleFileSelect, false);
			if (document.getElementById('files')) {
				document.getElementById('files')
					.addEventListener('change', handleFileSelect, false);
			}
			$('#open_btn')
				.on('click', function() {
					$.FileDialog({
						accept: ".xml",
						cancelButton: "Close",
						dragMessage: "Drop files here",
						dropheight: 400,
						errorMessage: "An error occured while loading file",
						multiple: false,
						okButton: "OK",
						readAs: "DataURL",
						removeMessage: "Remove file",
						title: "Load file"
					});
				})
				.on('files.bs.filedialog', function(ev) {
					var files_list = ev.files;
					handleFileSelect(files_list);
				})
				.on('cancel.bs.filedialog', function(ev) {
					handleFileSelect(ev);
				});
		}
	},
	setElementActions: {
		value: function setElementActions(){
			this.setFileBrowser();
			this.startTour();
			this.addBindings();
			this.addEventHandlers();
			this.tradeInfo.show();
		}
	},
	addBindings: {
		value: function addBindings(){
			var that = this;
			var stop = function stop(e) {
				if (e) {
					e.preventDefault();
				}
				that.bot.stop();
			};

			var logout = function logout() {
				account.logoutAllTokens(function(){
					that.updateTokenList();
					that.observer.emit('ui.log.info', that.translator.translateText('Logged you out!'));
				});
			};

			$('#stopButton')
				.click(stop)
				.hide();

			$('.panelExitButton')
				.click(function () {
					$(this)
						.parent()
						.hide();
				});

			$('.panel')
				.hide();

			$('.panel')
				.drags();

			$('#chart')
				.mousedown(function (e) { // prevent chart to trigger draggable
					e.stopPropagation();
				});

			$('table')
				.mousedown(function (e) { // prevent tables to trigger draggable
					e.stopPropagation();
				});

			$('#saveXml')
				.click(function (e) {
					that.blockly.saveXml();
				});

			$('#undo')
				.click(function (e) {
					that.blockly.undo();
				});

			$('#redo')
				.click(function (e) {
					that.blockly.redo();
				});

			$('#showSummary')
				.click(function (e) {
					$('#summaryPanel')
						.show();
				});

		 $('#loadXml')
				.click(function (e) {
					$('#fileBrowser')
						.show();
				});

			$('#logout')
				.click(function (e) {
					logout();
					$('.logout').hide();
				});

			$('#runButton')
				.click(function (e) {
					$('#stopButton').show();
					$('#runButton').hide();
					that.blockly.run();
				});

			$('#login')
				.bind('click.login', function(e){
					document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' + storageManager.get('appId') + '&l=' + that.translator.getLanguage().toUpperCase();
				})
				.text('Log in');

			$(document).keydown(function(e) {
				switch(e.which) {
					case 189: // -
						if ( e.ctrlKey ) {
							that.blockly.zoomOnPlusMinus(false);
						}
						break;
					case 187: // +
						if ( e.ctrlKey ) {
							that.blockly.zoomOnPlusMinus(true);
						}
						break;
					case 39: // right
						if (that.activeTour) {
							that.activeTour.next();
						} else {
							return;
						}
						break;
					default: return; // exit this handler for other keys
				}
				e.preventDefault(); // prevent the default action (scroll / move caret)
			});

		}
	},
	updateChart: {
		value: function updateChart(info) {
			var chartOptions = {
				type: 'area',
				theme: 'light',
				ticks: info.ticks,
			};
			if (this.latestOpenContract) {
				chartOptions.contract = this.latestOpenContract;
				if (this.latestOpenContract.is_sold) {
					delete this.latestOpenContract;
				}
			}
			chartOptions.pipSize = Number(Number(info.pip)
				.toExponential()
				.substring(3));
			if (!this.chart) {
				this.chart = Chart('chart', chartOptions);
			} else {
				this.chart.updateChart(chartOptions);
			}
		}
	},
	addEventHandlers: {
		value: function addEventHandlers() {
			var that = this;

			this.observer.register('api.error', function(error){
				if (error.code === 'InvalidToken'){
					storageManager.removeAllTokens();
					that.updateTokenList();
				}
				that.bot.stop();
				that.observer.emit('ui.error', error);
			});

			this.observer.register('bot.stop', function(tradeInfo){
				$('#runButton').show();
				$('#stopButton').hide();
			});

			this.observer.register('bot.tradeInfo', function(tradeInfo){
				_.extend(that.tradeInfo.tradeInfo, tradeInfo);
				that.tradeInfo.update();
			});

			this.observer.register('bot.tradeUpdate', function(contract){
				that.latestOpenContract = contract;
			});

			this.observer.register('bot.finish', function(contract){
				that.tradeInfo.add(contract);
			});

			this.observer.register('bot.tickUpdate', function(info){
				that.updateChart(info);
			});
		}
	}
});

module.exports = View;
<commit_msg>Match the new error from live api<commit_after>'use strict';
import logger from './logger';
import TradeInfo from './tradeInfo';
import account from 'binary-common-utils/account';
import Observer from 'binary-common-utils/observer';
import _Blockly from './blockly';
import storageManager from 'binary-common-utils/storageManager';
import Translator from 'translator';
import Bot from '../bot';
import Introduction from './tours/introduction';
import Welcome from './tours/welcome';
import {PlainChart as Chart} from 'binary-charts';
import _ from 'underscore';

var View = function View(){
	if ( View.instance ) {
		return View.instance;
	}
	this.observer = new Observer();
	View.instance = this;
	this.tours = {};
	this.translator = new Translator();
	this.tradeInfo = new TradeInfo();
	this.addTranslationToUi();
	this.errorAndLogHandling();
	var that = this;
	this.bot = new Bot();
	this.initPromise = new Promise(function(resolve, reject){
		that.updateTokenList();
		that.blockly = new _Blockly();
		that.blockly.initPromise.then(function(){
			that.setElementActions();
			that.initTours();
			resolve();
		});
	});
};

View.prototype = Object.create(null, {
	updateTokenList: {
		value: function updateTokenList() {
			var tokenList = storageManager.getTokenList();
			if (tokenList.length === 0) {
				$('#login').css('display', 'inline-block');
				$('#accountSelect').css('display', 'none');
				$('#logout').css('display', 'none');
			} else {
				$('#login').css('display', 'none');
				$('#accountSelect').css('display', 'inline-block');
				$('#logout').css('display', 'inline-block');
				tokenList.forEach(function (tokenInfo) {
					var str;
					if ( tokenInfo.hasOwnProperty('isVirtual') ) {
						str = (tokenInfo.isVirtual) ? 'Virtual Account' : 'Real Account';
					} else {
						str = '';
					}
					$('#accountSelect').append('<option value="' + tokenInfo.token + '">'+str + ' (' + tokenInfo.account_name+ ') ' + '</option>');
				});
			}
		}
	},
	addTranslationToUi: {
		value: function addTranslationToUi(){
			var that = this;
			$('[data-i18n-text]')
				.each(function() {
						var contents = $(this).contents();
						if (contents.length > 0) {
								if (contents.get(0).nodeType == Node.TEXT_NODE) {
										$(this).text(that.translator.translateText($(this)
								.attr('data-i18n-text')))
								.append(contents.slice(1));
								}
						} else {
						$(this)
							.text(that.translator.translateText($(this)
								.attr('data-i18n-text')));
					}
				});
		}
	},
	initTours: {
		value: function initTours() {
			this.tours.introduction = new Introduction();
			this.tours.welcome = new Welcome();
		}
	},
	startTour: {
		value: function startTour() {
			var that = this;
			$('#tours').on('change', function(e) {
					var value = $(this).val();
					if (value === '') return;
					if (that.activeTour) {
						that.activeTour.stop();
					}
					that.activeTour = that.tours[value];
					that.activeTour.start(function(){
						that.activeTour = null;
					});
			});
		}
	},
	errorAndLogHandling: {
		value: function errorAndLogHandling(){

			var that = this;

			this.observer.register('ui.error', function showError(error) {
				if (error.stack) {
					console.error(error.stack);
					if (logger.isDebug()) {
						console.log('%c' + error.stack, 'color: red');
					} else {
						logger.addLogToQueue('%c' + error.stack, 'color: red');
					}
				} else {
					console.error(error);
				}
				var message;
				if (error.error) {
					message = error.error.message;
				} else {
					message = error;
				}
				$.notify(message, {
					position: 'bottom right',
					className: 'error',
				});
				if (logger.isDebug()) {
					console.log('%cError: ' + message, 'color: red');
				} else {
					logger.addLogToQueue('%cError: ' + message, 'color: red');
				}
			});

			var observeForLog = function observeForLog(type, position) {
				var subtype = ( position === 'left' )? '.left' : '';
				that.observer.register('ui.log.' + type + subtype , function(message){
					if ( type === 'warn' ) {
						console.warn(message);
					}
					$.notify(message, {
						position: 'bottom ' + position,
						className: type,
					});
					if (logger.isDebug()) {
						console.log(message);
					} else {
						logger.addLogToQueue(message);
					}
				});
			};

			["success", "info", "warn", "error"].forEach(function(type){
				observeForLog(type, 'right');
				observeForLog(type, 'left');
			});

		}
	},
	setFileBrowser: {
		value: function setFileBrowser(){
			var that = this;
			var handleFileSelect = function handleFileSelect(e) {
				var files;
				if (e.type === 'drop') {
					e.stopPropagation();
					e.preventDefault();
					files = e.dataTransfer.files;
				} else {
					files = e.target.files;
				}
				files = Array.prototype.slice.apply(files);
				var file = files[0];
				if (file) {
					if (file.type.match('text/xml')) {
						readFile(file);
					} else {
						that.observer.emit('ui.log.info', that.translator.translateText('File is not supported:' + ' ') + file.name);
					}
				}
			};

			var readFile = function readFile(f) {
				var reader = new FileReader();
				reader.onload = (function (theFile) {
					$('#fileBrowser').hide();
					return function (e) {
						try {
							that.blockly.loadBlocksFile(e.target.result);
							that.observer.emit('ui.log.success', that.translator.translateText('Blocks are loaded successfully'));
						} catch (err) {
							that.observer.emit('ui.error', err);
						}
					};
				})(f);
				reader.readAsText(f);
			};

			var handleDragOver = function handleDragOver(e) {
				e.stopPropagation();
				e.preventDefault();
				e.dataTransfer.dropEffect = 'copy';
			};

			var dropZone = document.getElementById('dropZone');

			dropZone.addEventListener('dragover', handleDragOver, false);
			dropZone.addEventListener('drop', handleFileSelect, false);
			if (document.getElementById('files')) {
				document.getElementById('files')
					.addEventListener('change', handleFileSelect, false);
			}
			$('#open_btn')
				.on('click', function() {
					$.FileDialog({
						accept: ".xml",
						cancelButton: "Close",
						dragMessage: "Drop files here",
						dropheight: 400,
						errorMessage: "An error occured while loading file",
						multiple: false,
						okButton: "OK",
						readAs: "DataURL",
						removeMessage: "Remove file",
						title: "Load file"
					});
				})
				.on('files.bs.filedialog', function(ev) {
					var files_list = ev.files;
					handleFileSelect(files_list);
				})
				.on('cancel.bs.filedialog', function(ev) {
					handleFileSelect(ev);
				});
		}
	},
	setElementActions: {
		value: function setElementActions(){
			this.setFileBrowser();
			this.startTour();
			this.addBindings();
			this.addEventHandlers();
			this.tradeInfo.show();
		}
	},
	addBindings: {
		value: function addBindings(){
			var that = this;
			var stop = function stop(e) {
				if (e) {
					e.preventDefault();
				}
				that.bot.stop();
			};

			var logout = function logout() {
				account.logoutAllTokens(function(){
					that.updateTokenList();
					that.observer.emit('ui.log.info', that.translator.translateText('Logged you out!'));
				});
			};

			$('#stopButton')
				.click(stop)
				.hide();

			$('.panelExitButton')
				.click(function () {
					$(this)
						.parent()
						.hide();
				});

			$('.panel')
				.hide();

			$('.panel')
				.drags();

			$('#chart')
				.mousedown(function (e) { // prevent chart to trigger draggable
					e.stopPropagation();
				});

			$('table')
				.mousedown(function (e) { // prevent tables to trigger draggable
					e.stopPropagation();
				});

			$('#saveXml')
				.click(function (e) {
					that.blockly.saveXml();
				});

			$('#undo')
				.click(function (e) {
					that.blockly.undo();
				});

			$('#redo')
				.click(function (e) {
					that.blockly.redo();
				});

			$('#showSummary')
				.click(function (e) {
					$('#summaryPanel')
						.show();
				});

		 $('#loadXml')
				.click(function (e) {
					$('#fileBrowser')
						.show();
				});

			$('#logout')
				.click(function (e) {
					logout();
					$('.logout').hide();
				});

			$('#runButton')
				.click(function (e) {
					$('#stopButton').show();
					$('#runButton').hide();
					that.blockly.run();
				});

			$('#login')
				.bind('click.login', function(e){
					document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' + storageManager.get('appId') + '&l=' + that.translator.getLanguage().toUpperCase();
				})
				.text('Log in');

			$(document).keydown(function(e) {
				switch(e.which) {
					case 189: // -
						if ( e.ctrlKey ) {
							that.blockly.zoomOnPlusMinus(false);
						}
						break;
					case 187: // +
						if ( e.ctrlKey ) {
							that.blockly.zoomOnPlusMinus(true);
						}
						break;
					case 39: // right
						if (that.activeTour) {
							that.activeTour.next();
						} else {
							return;
						}
						break;
					default: return; // exit this handler for other keys
				}
				e.preventDefault(); // prevent the default action (scroll / move caret)
			});

		}
	},
	updateChart: {
		value: function updateChart(info) {
			var chartOptions = {
				type: 'area',
				theme: 'light',
				ticks: info.ticks,
			};
			if (this.latestOpenContract) {
				chartOptions.contract = this.latestOpenContract;
				if (this.latestOpenContract.is_sold) {
					delete this.latestOpenContract;
				}
			}
			chartOptions.pipSize = Number(Number(info.pip)
				.toExponential()
				.substring(3));
			if (!this.chart) {
				this.chart = Chart('chart', chartOptions);
			} else {
				this.chart.updateChart(chartOptions);
			}
		}
	},
	addEventHandlers: {
		value: function addEventHandlers() {
			var that = this;

			this.observer.register('api.error', function(error){
				if (error.code === 'InvalidToken'){
					storageManager.removeAllTokens();
					that.updateTokenList();
				}
				that.bot.stop();
				that.observer.emit('ui.error', error);
			});

			this.observer.register('bot.stop', function(tradeInfo){
				$('#runButton').show();
				$('#stopButton').hide();
			});

			this.observer.register('bot.tradeInfo', function(tradeInfo){
				_.extend(that.tradeInfo.tradeInfo, tradeInfo);
				that.tradeInfo.update();
			});

			this.observer.register('bot.tradeUpdate', function(contract){
				that.latestOpenContract = contract;
			});

			this.observer.register('bot.finish', function(contract){
				that.tradeInfo.add(contract);
			});

			this.observer.register('bot.tickUpdate', function(info){
				that.updateChart(info);
			});
		}
	}
});

module.exports = View;
<|endoftext|>


<commit_before>var state = require('lib/state'),
  ToolPane = require('views/tool_pane'),
  SettingsPane = require('views/settings_pane'),
  TitleBar = require('views/title_bar');

var Window = Backbone.View.extend({
  template: require('templates/window'),
  className: 'container',

  events: {
    'dragenter' : 'handleWindowDrag',
    'dragover' : 'handleWindowDrag',
    'dragover .add-to-chain' : 'handleAddToChain',
    'dragover .add-new-chain' : 'handleNewChain',
    'dragleave .add-to-chain' : 'handleAddToChain',
    'dragleave .add-new-chain' : 'handleNewChain',
    'drop .add-to-chain' : 'dropAdd',
    'drop .add-new-chain' : 'dropNew'
  },

  initialize: function(options) {
    if (options.dashboard) {
      this.dashboard = options.dashboard;
      this.listenTo(this.dashboard, 'change:zoom', this.render);
    }

    this.titleBar = new TitleBar({model: this.model});
    this.toolPane = new ToolPane({model: this.model});
    this.settingsPane = new SettingsPane({model: this.model});

    this.listenTo(this.titleBar, 'close', this.close);
    this.listenTo(this.model, 'change:settings_active', this.toggleSettings)
  },

  close: function() {
    this.model.destroy();
    this.remove();
  },

  handleWindowDrag: function(ev) {
    this.$('.drag-target').show();
    return false;
  },

  handleAddToChain: function(ev) {
    return false;
  },

  handleNewChain: function(ev) {
    return false;
  },

  toggleSettings: function(m, sa) {
    if (sa)
      this.$('.settings-pane').addClass('active');
    else
      this.$('.settings-pane').removeClass('active');
  },

  dropAdd: function(ev) {
    ev = ev.originalEvent;
    ev.preventDefault();
    this.$('.drag-target').show();

    var tool = JSON.parse(ev.dataTransfer.getData('application/json'));
    this.model.createChild(tool);
  },

  dropNew: function(ev) {
    ev = ev.originalEvent;
    ev.preventDefault();
    this.$('.drag-target').show();
  },

  render: function() {
    this.$el.html(this.template());
    this.$('.title-bar').html(this.titleBar.render().el);
    if (!this.dashboard || this.dashboard.get('zoom') === 1) {
      this.$('.tool-pane').html("
" + this.model.get('name') + "
");
    } else {
      this.$('.tool-pane').html(this.toolPane.render().el);
      this.$('.settings-pane').html(this.settingsPane.render().el);
    }

    if (this.model.get('settings_active'))
      this.$('.settings-pane').addClass('active')

    this.titleBar.delegateEvents();
    this.toolPane.delegateEvents();
    this.settingsPane.delegateEvents();
    return this;
  }
});

module.exports = Window;
<commit_msg>Fix showing drag targets<commit_after>var state = require('lib/state'),
  ToolPane = require('views/tool_pane'),
  SettingsPane = require('views/settings_pane'),
  TitleBar = require('views/title_bar');

var Window = Backbone.View.extend({
  template: require('templates/window'),
  className: 'container',

  events: {
    'dragover' : 'showTargets',
    'dragleave' : 'hideTargets',
    'dragover .add-to-chain' : 'handleAddToChain',
    'dragover .add-new-chain' : 'handleNewChain',
    'drop .add-to-chain' : 'dropAdd',
    'drop .add-new-chain' : 'dropNew'
  },

  initialize: function(options) {
    if (options.dashboard) {
      this.dashboard = options.dashboard;
      this.listenTo(this.dashboard, 'change:zoom', this.render);
    }

    this.titleBar = new TitleBar({model: this.model});
    this.toolPane = new ToolPane({model: this.model});
    this.settingsPane = new SettingsPane({model: this.model});

    this.listenTo(this.titleBar, 'close', this.close);
    this.listenTo(this.model, 'change:settings_active', this.toggleSettings)
  },

  close: function() {
    this.model.destroy();
    this.remove();
  },

  showTargets: function(ev) {
    this.$('.drag-target').show();
  },

  hideTargets: function(ev) {
    this.$('.drag-target').hide();
  },

  handleAddToChain: function(ev) {
    this.$('.drag-target').show();
    return false;
  },

  handleNewChain: function(ev) {
    this.$('.drag-target').show();
    return false;
  },

  toggleSettings: function(m, sa) {
    if (sa)
      this.$('.settings-pane').addClass('active');
    else
      this.$('.settings-pane').removeClass('active');
  },

  dropAdd: function(ev) {
    ev = ev.originalEvent;
    ev.preventDefault();
    this.$('.drag-target').hide();
    var tool = JSON.parse(ev.dataTransfer.getData('application/json'));
    this.model.createChild(tool);
  },

  dropNew: function(ev) {
    ev = ev.originalEvent;
    ev.preventDefault();
    this.$('.drag-target').hide();
  },

  render: function() {
    this.$el.html(this.template());
    this.$('.title-bar').html(this.titleBar.render().el);
    if (!this.dashboard || this.dashboard.get('zoom') === 1) {
      this.$('.tool-pane').html("
" + this.model.get('name') + "
");
    } else {
      this.$('.tool-pane').html(this.toolPane.render().el);
      this.$('.settings-pane').html(this.settingsPane.render().el);
    }

    if (this.model.get('settings_active'))
      this.$('.settings-pane').addClass('active')

    this.titleBar.delegateEvents();
    this.toolPane.delegateEvents();
    this.settingsPane.delegateEvents();
    return this;
  }
});

module.exports = Window;
<|endoftext|>


<commit_before>const ParserCommon = require('./ParserCommon');

function PropertyValue(xml) {
  this.Annotations = {};

  this.validElements = {
  };
  this.validAttributes = {
    'Bool': {bool: true},
    'String': {},
    'Path': {},
    'Property': {alreadyHandeled: true},
    'EnumMember': {}
  };

  var init = ParserCommon.initEntity.bind(this);
  init(xml, 'PropertyValue', 'Property');

  return this;
}

module.exports = PropertyValue;
/* vim: set tabstop=2 shiftwidth=2 expandtab: */
<commit_msg>Allowing for Records to be within PropertyValues<commit_after>const ParserCommon = require('./ParserCommon');

function PropertyValue(xml) {
  this.Annotations = {};
  this.Records = [];

  this.validElements = {
    'Record': {parent: this.Records}
  };
  this.validAttributes = {
    'Bool': {bool: true},
    'String': {},
    'Path': {},
    'Property': {alreadyHandeled: true},
    'EnumMember': {}
  };

  var init = ParserCommon.initEntity.bind(this);
  init(xml, 'PropertyValue', 'Property');

  return this;
}

module.exports = PropertyValue;
/* vim: set tabstop=2 shiftwidth=2 expandtab: */
<|endoftext|>


<commit_before>'use babel';

import React from 'react';
import {Transformime} from 'transformime';
import {
	StreamTransformer,
	TracebackTransformer,
	MarkdownTransformer,
}
from 'transformime-jupyter-transformers';

export default class DisplayArea extends React.Component {

  constructor(props) {
      super(props);
    this.transformer = new Transformime();
    this.transformer.transformers.push(new StreamTransformer());
    this.transformer.transformers.push(new TracebackTransformer());
      this.transformer.transformers.push(new MarkdownTransformer());
  }

  componentDidMount() {
    this.setupDOM();
  }

  // transformime-generated HTML is in outputs
  // after every render, fill our element with that HTML
  componentDidUpdate() {
    let outputs = this.props.data.get('outputs');
    let outputHTML = outputs.join('');
    this.el.innerHTML = outputHTML;
  }

  render() {
    return (

    );
  }

  setupDOM() {
    let container = React.findDOMNode(this);
    let outputNode = document.createElement('div');
    outputNode.style.backgroundColor = 'white';
    this.shadow = container.createShadowRoot();
    this.shadow.appendChild(outputNode);
    this.document = this.shadow.ownerDocument;
    this.el = outputNode;
  }

}
<commit_msg>Enable copy on cell output<commit_after>'use babel';

import React from 'react';
import {Transformime} from 'transformime';
import {
	StreamTransformer,
	TracebackTransformer,
	MarkdownTransformer,
}
from 'transformime-jupyter-transformers';

export default class DisplayArea extends React.Component {

  constructor(props) {
      super(props);
    this.transformer = new Transformime();
    this.transformer.transformers.push(new StreamTransformer());
    this.transformer.transformers.push(new TracebackTransformer());
      this.transformer.transformers.push(new MarkdownTransformer());
  }

  componentDidMount() {
    this.setupDOM();
  }

  // transformime-generated HTML is in outputs
  // after every render, fill our element with that HTML
  componentDidUpdate() {
    let outputs = this.props.data.get('outputs');
    let outputHTML = outputs.join('');
    this.el.innerHTML = outputHTML;
  }

  render() {
    return (

    );
  }

  setupDOM() {
    let container = React.findDOMNode(this);
    let outputNode = document.createElement('div');
    outputNode.style.backgroundColor = 'white';
    this.shadow = container.createShadowRoot();
    this.shadow.appendChild(outputNode);
    this.document = this.shadow.ownerDocument;
    this.el = outputNode;
  }

}
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
  Methods for EC2 instance
*/

var Path = require('path')
  , Optionall = require('optionall')
  , FSTK = require('fstk')
  , Async = require('async')
  , _ = require('underscore')
  , Belt = require('jsbelt')
  , Util = require('util')
  , Winston = require('winston')
  , Events = require('events')
  , Request = require('request')
  , EC2 = require('./ec2.js')
  , S3 = require('./s3.js')
;

module.exports = function(O){
  var Opts = O || new Optionall({
                                  '__dirname': Path.resolve(module.filename + '/../..')
                                , 'file_priority': ['package.json', 'environment.json', 'config.json']
                                });

  var S = new (Events.EventEmitter.bind({}))();
  S.settings = Belt.extend({
    'log_level': 'info'
  , 'instance_data_url': 'http://169.254.169.254'
  }, Opts);

  S['ec2'] = new EC2(S.settings.aws);
  S['s3'] = new S3(S.settings.aws);

  var log = Opts.log || new Winston.Logger();
  if (!Opts.log) log.add(Winston.transports.Console, {'level': S.settings.log_level, 'colorize': true, 'timestamp': false});

  /*
    get information on this instance
  */
  S['describeInstance'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      'instance_data_url': self.settings.instance_data_url
    });

    var gb = {};
    return Async.waterfall([
      function(cb){
        return Request(a.o.instance_data_url + '/latest/meta-data/instance-id', Belt.cs(cb, gb, 'id', 2, 0));
      }
    , function(cb){
        return self.ec2._API.describeInstances({
          'InstanceIds': [gb.id]
        }, Belt.dcds(cb, gb, 'inst', 1, 'Reservations.0.Instances.0', 0));
      }
    ], function(err){
      return a.cb(err, gb.inst);
    });
  };

  /*
    get ebs volume id for a device
  */
  S['deviceVolumeId'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      'device': '/dev/sda'
    });

    if (!Belt.call(a.o, 'device.match', /^\/dev\//)) a.o.device = '/dev/' + a.o.device;
    a.o.device = a.o.device.replace(/^\/dev\/xvd/, '/dev/sd');

    return self.describeInstance(a.o, function(err, inst){
      if (err) return a.cb(err);

      var devs = Belt.get(inst, 'BlockDeviceMappings') || []
        , id = _.find(devs, function(d){ return Belt.get(d, 'DeviceName') === a.o.device; });

      id = Belt.get(id, 'Ebs.VolumeId');

      return a.cb(id ? undefined : new Error('VolumeId not found'), id);
    });
  };

  /*
    create expiring snapshot of a device
  */
  S['createDeviceSnapshot'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      'device': '/dev/sda'
    , 'days': 7 //days to wait before expiring
    , 'autoexpire': true //expire any expired snapshots on this account
    });

    var gb = {};
    return Async.waterfall([
      function(cb){
        if (!a.o.autoexpire) return cb();
        return EC2.deleteExpiredSnapshots(Belt.cw(cb));
      }
    , function(cb){
        return self.deviceVolumeId(a.o, Belt.cs(cb, gb, 'id', 1, 0));
      }
    , function(cb){
        return EC2.createExpiringSnapshot(gb.id, a.o.days, Belt.cw(cb, 0));
      }
    ], return a.cb);
  };

  return S;
};

if (require.main === module){
  var M = new module.exports();

  if (M.settings.argv.describeInstance) M.describeInstance(M.settings.argv, function(err, inst){
    if (err){
      console.error(err);
    } else {
      console.log(Belt.stringify(inst));
    }
    return process.exit(err ? 1 : 0);
  });

  if (M.settings.argv.deviceVolumeId) M.deviceVolumeId(M.settings.argv, function(err, id){
    if (err){
      console.error(err);
    } else {
      console.log(Belt.stringify(id));
    }
    return process.exit(err ? 1 : 0);
  });

  if (M.settings.argv.createDeviceSnapshot) M.createDeviceSnapshot(M.settings.argv, function(err){
    if (err){
      console.error(err);
    }
    return process.exit(err ? 1 : 0);
  });
}
<commit_msg>ec2 instance methods<commit_after>#!/usr/bin/env node

/*
  Methods for EC2 instance
*/

var Path = require('path')
  , Optionall = require('optionall')
  , FSTK = require('fstk')
  , Async = require('async')
  , _ = require('underscore')
  , Belt = require('jsbelt')
  , Util = require('util')
  , Winston = require('winston')
  , Events = require('events')
  , Request = require('request')
  , EC2 = require('./ec2.js')
  , S3 = require('./s3.js')
;

module.exports = function(O){
  var Opts = O || new Optionall({
                                  '__dirname': Path.resolve(module.filename + '/../..')
                                , 'file_priority': ['package.json', 'environment.json', 'config.json']
                                });

  var S = new (Events.EventEmitter.bind({}))();
  S.settings = Belt.extend({
    'log_level': 'info'
  , 'instance_data_url': 'http://169.254.169.254'
  }, Opts);

  S['ec2'] = new EC2(S.settings.aws);
  S['s3'] = new S3(S.settings.aws);

  var log = Opts.log || new Winston.Logger();
  if (!Opts.log) log.add(Winston.transports.Console, {'level': S.settings.log_level, 'colorize': true, 'timestamp': false});

  /*
    get information on this instance
  */
  S['describeInstance'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      'instance_data_url': self.settings.instance_data_url
    });

    var gb = {};
    return Async.waterfall([
      function(cb){
        return Request(a.o.instance_data_url + '/latest/meta-data/instance-id', Belt.cs(cb, gb, 'id', 2, 0));
      }
    , function(cb){
        return self.ec2._API.describeInstances({
          'InstanceIds': [gb.id]
        }, Belt.dcds(cb, gb, 'inst', 1, 'Reservations.0.Instances.0', 0));
      }
    ], function(err){
      return a.cb(err, gb.inst);
    });
  };

  /*
    get ebs volume id for a device
  */
  S['deviceVolumeId'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      'device': '/dev/sda'
    });

    if (!Belt.call(a.o, 'device.match', /^\/dev\//)) a.o.device = '/dev/' + a.o.device;
    a.o.device = a.o.device.replace(/^\/dev\/xvd/, '/dev/sd');

    return self.describeInstance(a.o, function(err, inst){
      if (err) return a.cb(err);

      var devs = Belt.get(inst, 'BlockDeviceMappings') || []
        , id = _.find(devs, function(d){ return Belt.get(d, 'DeviceName') === a.o.device; });

      id = Belt.get(id, 'Ebs.VolumeId');

      return a.cb(id ? undefined : new Error('VolumeId not found'), id);
    });
  };

  /*
    create expiring snapshot of a device
  */
  S['createDeviceSnapshot'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      'device': '/dev/sda'
    , 'days': 7 //days to wait before expiring
    , 'autoexpire': true //expire any expired snapshots on this account
    });

    var gb = {};
    return Async.waterfall([
      function(cb){
        if (!a.o.autoexpire) return cb();
        return EC2.deleteExpiredSnapshots(Belt.cw(cb));
      }
    , function(cb){
        return self.deviceVolumeId(a.o, Belt.cs(cb, gb, 'id', 1, 0));
      }
    , function(cb){
        return EC2.createExpiringSnapshot(gb.id, a.o.days, Belt.cw(cb, 0));
      }
    ], a.cb);
  };

  return S;
};

if (require.main === module){
  var M = new module.exports();

  if (M.settings.argv.describeInstance) M.describeInstance(M.settings.argv, function(err, inst){
    if (err){
      console.error(err);
    } else {
      console.log(Belt.stringify(inst));
    }
    return process.exit(err ? 1 : 0);
  });

  if (M.settings.argv.deviceVolumeId) M.deviceVolumeId(M.settings.argv, function(err, id){
    if (err){
      console.error(err);
    } else {
      console.log(Belt.stringify(id));
    }
    return process.exit(err ? 1 : 0);
  });

  if (M.settings.argv.createDeviceSnapshot) M.createDeviceSnapshot(M.settings.argv, function(err){
    if (err){
      console.error(err);
    }
    return process.exit(err ? 1 : 0);
  });
}
<|endoftext|>