<commit_before>var util         = require('util');
var EventEmitter = require('events').EventEmitter;
var RippleError  = require('./rippleerror').RippleError;
var PendingQueue = require('./transactionqueue').TransactionQueue;

/**
 * @constructor TransactionManager
 * @param {Account} account
 */

function TransactionManager(account) {
  EventEmitter.call(this);

  var self = this;

  this._account           = account;
  this._accountID         = account._account_id;
  this._remote            = account._remote;
  this._pending           = new PendingQueue;
  this._nextSequence      = void(0);
  this._cache             = { };
  this._sequenceCache     = { };
  this._maxFee            = this._remote.max_fee;
  this._submissionTimeout = this._remote._submission_timeout;

  // Query remote server for next account
  // transaction sequence number
  this._loadSequence();

  function transactionReceived(res) {
    var transaction = TransactionManager.normalizeTransaction(res);
    var sequence    = transaction.transaction.Sequence;
    var hash        = transaction.transaction.hash;

    self._sequenceCache[sequence] = transaction;

    var pending = self._pending.get('_hash', hash);

    self._remote._trace('transactionmanager: transaction_received: %s', transaction.transaction);

    if (pending) {
      pending.emit('success', transaction);
    } else {
      self._cache[hash] = transaction;
    }
  };

  this._account.on('transaction-outbound', transactionReceived);

  function adjustFees() {
    self._pending.forEach(function(pending) {
      if (self._remote.local_fee && pending.tx_json.Fee) {
        var oldFee = pending.tx_json.Fee;
        var newFee = self._remote.feeTx(pending.fee_units()).to_json();
        pending.tx_json.Fee = newFee;
        pending.emit('fee_adjusted', oldFee, newFee);
        self._remote._trace('transactionmanager: adjusting_fees: %s', pending, oldFee, newFee);
      }
    });
  };

  this._remote.on('load_changed', adjustFees);

  function updatePendingStatus(ledger) {
    self._pending.forEach(function(pending) {
      pending.lastLedger = ledger;
      switch (ledger.ledger_index - pending.submitIndex) {
        case 8:
          pending.emit('lost', ledger);
          pending.emit('error', new RippleError('tejLost', 'Transaction lost'));
          self._remote._trace('transactionmanager: update_pending: %s', pending.tx_json);
          break;
        case 4:
          pending.set_state('client_missing');
          pending.emit('missing', ledger);
          break;
      }
    });
  };

  this._remote.on('ledger_closed', updatePendingStatus);

  function remoteReconnected() {
    //Load account transaction history
    var options = {
      account: self._accountID,
      ledger_index_min: -1,
      ledger_index_max: -1,
      limit: 10
    }

    self._remote.requestAccountTx(options, function(err, transactions) {
      if (!err && transactions.transactions) {
        transactions.transactions.forEach(transactionReceived);
      }
      self._remote.on('ledger_closed', updatePendingStatus);
    });

    //Load next transaction sequence
    self._loadSequence(function sequenceLoaded() {
      self._resubmit(3);
    });

    self.emit('reconnect');
  };

  function remoteDisconnected() {
    self._remote.once('connect', remoteReconnected);
    self._remote.removeListener('ledger_closed', updatePendingStatus);
  };

  this._remote.on('disconnect', remoteDisconnected);
};

util.inherits(TransactionManager, EventEmitter);

//Normalize transactions received from account
//transaction stream and account_tx
TransactionManager.normalizeTransaction = function(tx) {
  var transaction = tx;

  if (!tx.engine_result) {
    // account_tx
    transaction = {
      engine_result:          tx.meta.TransactionResult,
      transaction:            tx.tx,
      hash:                   tx.tx.hash,
      ledger_index:           tx.tx.ledger_index,
      meta:                   tx.meta,
      type:                   'transaction',
      validated:              true
    }
    transaction.result         = transaction.engine_result;
    transaction.result_message = transaction.engine_result_message;
  }

  transaction.metadata = transaction.meta;

  return transaction;
};

//Fill an account transaction sequence
TransactionManager.prototype._fillSequence = function(tx, callback) {
  var fill = this._remote.transaction();
  fill.account_set(this._accountID);
  fill.tx_json.Sequence = tx.tx_json.Sequence - 1;
  fill.submit(callback);
};

TransactionManager.prototype._loadSequence = function(callback) {
  var self = this;

  function sequenceLoaded(err, sequence) {
    if (typeof sequence === 'number') {
      self._nextSequence = sequence;
      self.emit('sequence_loaded', sequence);
    } else {
      return setTimeout(function() {
        self._loadSequence(callback);
      }, 1000 * 3);
    }
    if (typeof callback === 'function') {
      callback(err, sequence);
    }
  };

  this._account.getNextSequence(sequenceLoaded);
};

TransactionManager.prototype._resubmit = function(waitLedgers, pending) {
  var self = this;
  var pending = pending ? [ pending ] : this._pending;

  if (waitLedgers) {
    var ledgers = Number(waitLedgers) || 3;
    this._waitLedgers(ledgers, function() {
      pending.forEach(resubmitTransaction);
    });
  } else {
    pending.forEach(resubmitTransaction);
  }

  function resubmitTransaction(pending) {
    if (!pending || pending.finalized) {
      // Transaction has been finalized, nothing to do
      return;
    }

    var hashCached = self._cache[pending._hash];
    self._remote._trace('transactionmanager: resubmit: %s', pending.tx_json);

    if (hashCached) {
      pending.emit('success', hashCached);
    } else {
      while (self._sequenceCache[pending.tx_json.Sequence]) {
        //Sequence number has been consumed by another transaction
        self._remote._trace('transactionmanager: incrementing sequence: %s', pending);
        pending.tx_json.Sequence += 1;
      }

      pending.once('submitted', function() {
        pending.emit('resubmitted');
        self._loadSequence();
      });

      self._request(pending);
    }
  }
};

TransactionManager.prototype._waitLedgers = function(ledgers, callback) {
  var self = this;
  var closes = 0;

  function ledgerClosed() {
    if (++closes === ledgers) {
      callback();
      self._remote.removeListener('ledger_closed', ledgerClosed);
    }
  };

  this._remote.on('ledger_closed', ledgerClosed);
};

TransactionManager.prototype._request = function(tx) {
  var self   = this;
  var remote = this._remote;

  if (tx.attempts > 10) {
    tx.emit('error', new RippleError('tejAttemptsExceeded'));
    return;
  }

  var submitRequest = remote.requestSubmit();

  submitRequest.build_path(tx._build_path);

  if (remote.local_signing) {
    tx.sign();
    submitRequest.tx_blob(tx.serialize().to_hex());
  } else {
    submitRequest.secret(tx._secret);
    submitRequest.tx_json(tx.tx_json);
  }

  tx._hash = tx.hash();

  remote._trace('transactionmanager: submit: %s', tx.tx_json);

  function transactionProposed(message) {
    tx.set_state('client_proposed');
    // If server is honest, don't expect a final if rejected.
    message.rejected = tx.isRejected(message.engine_result_code)
    tx.emit('proposed', message);
  };

  function transactionFailed(message) {
    switch (message.engine_result) {
      case 'tefPAST_SEQ':
        self._resubmit(3, tx);
      break;
      default:
        submissionError(message);
    }
  };

  function transactionRetry(message) {
    switch (message.engine_result) {
      case 'terPRE_SEQ':
        self._fillSequence(tx, function() {
          self._resubmit(2, tx);
        });
        break;
      default:
        self._resubmit(1, tx);
    }
  };

  function transactionFeeClaimed(message) {
    tx.emit('error', message);
  };

  function submissionError(error) {
    if (self._is_too_busy(error)) {
      self._resubmit(1, tx);
    } else {
      self._nextSequence--;
      tx.set_state('remoteError');
      tx.emit('submitted', error);
      tx.emit('error', error);
    }
  };

  function submissionSuccess(message) {
    if (message.tx_json.hash) {
      tx._hash = message.tx_json.hash;
    }

    remote._trace('transactionmanager: submit_response: %s', message);

    message.result = message.engine_result || '';

    tx.emit('submitted', message);

    switch (message.result.slice(0, 3)) {
      case 'tec':
        transactionFeeClaimed(message);
        break;
      case 'tes':
        transactionProposed(message);
        break;
      case 'tef':
        transactionFailed(message);
        break;
      case 'ter':
        transactionRetry(message);
        break;
      default:
        submissionError(message);
    }
  };

  submitRequest.once('success', submissionSuccess);
  submitRequest.once('error', submissionError);

  submitRequest.timeout(this._submissionTimeout, function() {
    tx.emit('timeout');
    if (remote._connected) {
      remote._trace('transactionmanager: timeout: %s', tx.tx_json);
      self._resubmit(3, tx);
    }
  });

  submitRequest.broadcast();

  tx.set_state('client_submitted');
  tx.attempts++;

  return submitRequest;
};

TransactionManager.prototype._is_remote_error = function(error) {
  return error && typeof error === 'object'
      && error.error === 'remoteError'
      && typeof error.remote === 'object'
};

TransactionManager.prototype._is_not_found = function(error) {
  return this._is_remote_error(error) && /^(txnNotFound|transactionNotFound)$/.test(error.remote.error);
};

TransactionManager.prototype._is_too_busy = function(error) {
  return this._is_remote_error(error) && error.remote.error === 'tooBusy';
};

/**
 * Entry point for TransactionManager submission
 *
 * @param {Object} tx
 */

TransactionManager.prototype.submit = function(tx) {
  var self = this;

  // If sequence number is not yet known, defer until it is.
  if (typeof this._nextSequence === 'undefined') {
    function sequenceLoaded() {
      self.submit(tx);
    };
    this.once('sequence_loaded', sequenceLoaded);
    return;
  }

  if (typeof tx.tx_json.Sequence !== 'number') {
    tx.tx_json.Sequence = this._nextSequence++;
  }

  tx.submitIndex = this._remote._ledger_current_index;
  tx.lastLedger  = void(0);
  tx.attempts    = 0;
  tx.complete();

  function finalize(message) {
    if (!tx.finalized) {
      self._pending.removeHash(tx._hash);
      remote._trace('transactionmanager: finalize_transaction: %s', tx.tx_json);
      tx.finalized = true;
      tx.emit('final', message);
    }
  };

  tx.on('error', finalize);
  tx.once('success', finalize);

  tx.once('abort', function() {
    tx.emit('error', new RippleError('tejAbort', 'Transaction aborted'));
  });

  var fee = Number(tx.tx_json.Fee);
  var remote = this._remote;

  if (!tx._secret && !tx.tx_json.TxnSignature) {
    tx.emit('error', new RippleError('tejSecretUnknown', 'Missing secret'));
  } else if (!remote.trusted && !remote.local_signing) {
    tx.emit('error', new RippleError('tejServerUntrusted', 'Attempt to give secret to untrusted server'));
  } else if (fee && fee > this._maxFee) {
    tx.emit('error', new RippleError('tejMaxFeeExceeded', 'Max fee exceeded'));
  } else {
    this._pending.push(tx);
    this._request(tx);
  }
};

exports.TransactionManager = TransactionManager;
<commit_msg>Avoid circular JSON stringification during transactionmanager trace<commit_after>var util         = require('util');
var EventEmitter = require('events').EventEmitter;
var RippleError  = require('./rippleerror').RippleError;
var PendingQueue = require('./transactionqueue').TransactionQueue;

/**
 * @constructor TransactionManager
 * @param {Account} account
 */

function TransactionManager(account) {
  EventEmitter.call(this);

  var self = this;

  this._account           = account;
  this._accountID         = account._account_id;
  this._remote            = account._remote;
  this._pending           = new PendingQueue;
  this._nextSequence      = void(0);
  this._cache             = { };
  this._sequenceCache     = { };
  this._maxFee            = this._remote.max_fee;
  this._submissionTimeout = this._remote._submission_timeout;

  // Query remote server for next account
  // transaction sequence number
  this._loadSequence();

  function transactionReceived(res) {
    var transaction = TransactionManager.normalizeTransaction(res);
    var sequence    = transaction.transaction.Sequence;
    var hash        = transaction.transaction.hash;

    self._sequenceCache[sequence] = transaction;

    var pending = self._pending.get('_hash', hash);

    self._remote._trace('transactionmanager: transaction_received: %s', transaction.transaction);

    if (pending) {
      pending.emit('success', transaction);
    } else {
      self._cache[hash] = transaction;
    }
  };

  this._account.on('transaction-outbound', transactionReceived);

  function adjustFees() {
    self._pending.forEach(function(pending) {
      if (self._remote.local_fee && pending.tx_json.Fee) {
        var oldFee = pending.tx_json.Fee;
        var newFee = self._remote.feeTx(pending.fee_units()).to_json();
        pending.tx_json.Fee = newFee;
        pending.emit('fee_adjusted', oldFee, newFee);
        self._remote._trace('transactionmanager: adjusting_fees: %s', pending, oldFee, newFee);
      }
    });
  };

  this._remote.on('load_changed', adjustFees);

  function updatePendingStatus(ledger) {
    self._pending.forEach(function(pending) {
      pending.lastLedger = ledger;
      switch (ledger.ledger_index - pending.submitIndex) {
        case 8:
          pending.emit('lost', ledger);
          pending.emit('error', new RippleError('tejLost', 'Transaction lost'));
          self._remote._trace('transactionmanager: update_pending: %s', pending.tx_json);
          break;
        case 4:
          pending.set_state('client_missing');
          pending.emit('missing', ledger);
          break;
      }
    });
  };

  this._remote.on('ledger_closed', updatePendingStatus);

  function remoteReconnected() {
    //Load account transaction history
    var options = {
      account: self._accountID,
      ledger_index_min: -1,
      ledger_index_max: -1,
      limit: 10
    }

    self._remote.requestAccountTx(options, function(err, transactions) {
      if (!err && transactions.transactions) {
        transactions.transactions.forEach(transactionReceived);
      }
      self._remote.on('ledger_closed', updatePendingStatus);
    });

    //Load next transaction sequence
    self._loadSequence(function sequenceLoaded() {
      self._resubmit(3);
    });

    self.emit('reconnect');
  };

  function remoteDisconnected() {
    self._remote.once('connect', remoteReconnected);
    self._remote.removeListener('ledger_closed', updatePendingStatus);
  };

  this._remote.on('disconnect', remoteDisconnected);
};

util.inherits(TransactionManager, EventEmitter);

//Normalize transactions received from account
//transaction stream and account_tx
TransactionManager.normalizeTransaction = function(tx) {
  var transaction = tx;

  if (!tx.engine_result) {
    // account_tx
    transaction = {
      engine_result:          tx.meta.TransactionResult,
      transaction:            tx.tx,
      hash:                   tx.tx.hash,
      ledger_index:           tx.tx.ledger_index,
      meta:                   tx.meta,
      type:                   'transaction',
      validated:              true
    }
    transaction.result         = transaction.engine_result;
    transaction.result_message = transaction.engine_result_message;
  }

  transaction.metadata = transaction.meta;

  return transaction;
};

//Fill an account transaction sequence
TransactionManager.prototype._fillSequence = function(tx, callback) {
  var fill = this._remote.transaction();
  fill.account_set(this._accountID);
  fill.tx_json.Sequence = tx.tx_json.Sequence - 1;
  fill.submit(callback);
};

TransactionManager.prototype._loadSequence = function(callback) {
  var self = this;

  function sequenceLoaded(err, sequence) {
    if (typeof sequence === 'number') {
      self._nextSequence = sequence;
      self.emit('sequence_loaded', sequence);
    } else {
      return setTimeout(function() {
        self._loadSequence(callback);
      }, 1000 * 3);
    }
    if (typeof callback === 'function') {
      callback(err, sequence);
    }
  };

  this._account.getNextSequence(sequenceLoaded);
};

TransactionManager.prototype._resubmit = function(waitLedgers, pending) {
  var self = this;
  var pending = pending ? [ pending ] : this._pending;

  if (waitLedgers) {
    var ledgers = Number(waitLedgers) || 3;
    this._waitLedgers(ledgers, function() {
      pending.forEach(resubmitTransaction);
    });
  } else {
    pending.forEach(resubmitTransaction);
  }

  function resubmitTransaction(pending) {
    if (!pending || pending.finalized) {
      // Transaction has been finalized, nothing to do
      return;
    }

    var hashCached = self._cache[pending._hash];
    self._remote._trace('transactionmanager: resubmit: %s', pending.tx_json);

    if (hashCached) {
      pending.emit('success', hashCached);
    } else {
      while (self._sequenceCache[pending.tx_json.Sequence]) {
        //Sequence number has been consumed by another transaction
        self._remote._trace('transactionmanager: incrementing sequence: %s', pending.tx_json);
        pending.tx_json.Sequence += 1;
      }

      pending.once('submitted', function() {
        pending.emit('resubmitted');
        self._loadSequence();
      });

      self._request(pending);
    }
  }
};

TransactionManager.prototype._waitLedgers = function(ledgers, callback) {
  var self = this;
  var closes = 0;

  function ledgerClosed() {
    if (++closes === ledgers) {
      callback();
      self._remote.removeListener('ledger_closed', ledgerClosed);
    }
  };

  this._remote.on('ledger_closed', ledgerClosed);
};

TransactionManager.prototype._request = function(tx) {
  var self   = this;
  var remote = this._remote;

  if (tx.attempts > 10) {
    tx.emit('error', new RippleError('tejAttemptsExceeded'));
    return;
  }

  var submitRequest = remote.requestSubmit();

  submitRequest.build_path(tx._build_path);

  if (remote.local_signing) {
    tx.sign();
    submitRequest.tx_blob(tx.serialize().to_hex());
  } else {
    submitRequest.secret(tx._secret);
    submitRequest.tx_json(tx.tx_json);
  }

  tx._hash = tx.hash();

  remote._trace('transactionmanager: submit: %s', tx.tx_json);

  function transactionProposed(message) {
    tx.set_state('client_proposed');
    // If server is honest, don't expect a final if rejected.
    message.rejected = tx.isRejected(message.engine_result_code)
    tx.emit('proposed', message);
  };

  function transactionFailed(message) {
    switch (message.engine_result) {
      case 'tefPAST_SEQ':
        self._resubmit(3, tx);
      break;
      default:
        submissionError(message);
    }
  };

  function transactionRetry(message) {
    switch (message.engine_result) {
      case 'terPRE_SEQ':
        self._fillSequence(tx, function() {
          self._resubmit(2, tx);
        });
        break;
      default:
        self._resubmit(1, tx);
    }
  };

  function transactionFeeClaimed(message) {
    tx.emit('error', message);
  };

  function submissionError(error) {
    if (self._is_too_busy(error)) {
      self._resubmit(1, tx);
    } else {
      self._nextSequence--;
      tx.set_state('remoteError');
      tx.emit('submitted', error);
      tx.emit('error', error);
    }
  };

  function submissionSuccess(message) {
    if (message.tx_json.hash) {
      tx._hash = message.tx_json.hash;
    }

    remote._trace('transactionmanager: submit_response: %s', message);

    message.result = message.engine_result || '';

    tx.emit('submitted', message);

    switch (message.result.slice(0, 3)) {
      case 'tec':
        transactionFeeClaimed(message);
        break;
      case 'tes':
        transactionProposed(message);
        break;
      case 'tef':
        transactionFailed(message);
        break;
      case 'ter':
        transactionRetry(message);
        break;
      default:
        submissionError(message);
    }
  };

  submitRequest.once('success', submissionSuccess);
  submitRequest.once('error', submissionError);

  submitRequest.timeout(this._submissionTimeout, function() {
    tx.emit('timeout');
    if (remote._connected) {
      remote._trace('transactionmanager: timeout: %s', tx.tx_json);
      self._resubmit(3, tx);
    }
  });

  submitRequest.broadcast();

  tx.set_state('client_submitted');
  tx.attempts++;

  return submitRequest;
};

TransactionManager.prototype._is_remote_error = function(error) {
  return error && typeof error === 'object'
      && error.error === 'remoteError'
      && typeof error.remote === 'object'
};

TransactionManager.prototype._is_not_found = function(error) {
  return this._is_remote_error(error) && /^(txnNotFound|transactionNotFound)$/.test(error.remote.error);
};

TransactionManager.prototype._is_too_busy = function(error) {
  return this._is_remote_error(error) && error.remote.error === 'tooBusy';
};

/**
 * Entry point for TransactionManager submission
 *
 * @param {Object} tx
 */

TransactionManager.prototype.submit = function(tx) {
  var self = this;

  // If sequence number is not yet known, defer until it is.
  if (typeof this._nextSequence === 'undefined') {
    function sequenceLoaded() {
      self.submit(tx);
    };
    this.once('sequence_loaded', sequenceLoaded);
    return;
  }

  if (typeof tx.tx_json.Sequence !== 'number') {
    tx.tx_json.Sequence = this._nextSequence++;
  }

  tx.submitIndex = this._remote._ledger_current_index;
  tx.lastLedger  = void(0);
  tx.attempts    = 0;
  tx.complete();

  function finalize(message) {
    if (!tx.finalized) {
      self._pending.removeHash(tx._hash);
      remote._trace('transactionmanager: finalize_transaction: %s', tx.tx_json);
      tx.finalized = true;
      tx.emit('final', message);
    }
  };

  tx.on('error', finalize);
  tx.once('success', finalize);

  tx.once('abort', function() {
    tx.emit('error', new RippleError('tejAbort', 'Transaction aborted'));
  });

  var fee = Number(tx.tx_json.Fee);
  var remote = this._remote;

  if (!tx._secret && !tx.tx_json.TxnSignature) {
    tx.emit('error', new RippleError('tejSecretUnknown', 'Missing secret'));
  } else if (!remote.trusted && !remote.local_signing) {
    tx.emit('error', new RippleError('tejServerUntrusted', 'Attempt to give secret to untrusted server'));
  } else if (fee && fee > this._maxFee) {
    tx.emit('error', new RippleError('tejMaxFeeExceeded', 'Max fee exceeded'));
  } else {
    this._pending.push(tx);
    this._request(tx);
  }
};

exports.TransactionManager = TransactionManager;
<|endoftext|>


<commit_before>'use strict';

var forEach = require('es5-ext/lib/Object/for-each')
  , ext = require('../_base').ext

ext.maxAge = function (maxAge, conf, options) {
	var cache, mode;

	maxAge = maxAge >>> 0;
	if (!maxAge) {
		return;
	}

	cache = {};
	mode = (options.async && ext.async) ? 'async' : '';
	conf.on('init' + mode, function (id) {
		cache[id] = setTimeout(function () { conf.clear(id); }, maxAge);
	});
	conf.on('purge', function (id) {
		clearTimeout(cache[id]);
		delete cache[id];
	});
	conf.on('purgeall', function (id) {
		forEach(cache, function (id) {
			clearTimeout(id);
		});
		cache = {};
	});
};
<commit_msg>maxAge: Rely on async events<commit_after>'use strict';

var forEach = require('es5-ext/lib/Object/for-each')
  , ext = require('../_base').ext

ext.maxAge = function (maxAge, conf, options) {
	var cache, mode;

	maxAge = maxAge >>> 0;
	if (!maxAge) {
		return;
	}

	cache = {};
	mode = (options.async && ext.async) ? 'async' : '';
	conf.on('init' + mode, function (id) {
		cache[id] = setTimeout(function () { conf.clear(id); }, maxAge);
	});
	conf.on('purge' + mode, function (id) {
		clearTimeout(cache[id]);
		delete cache[id];
	});

	if (!async) {
		conf.on('purgeall', function (id) {
			forEach(cache, function (id) {
				clearTimeout(id);
			});
			cache = {};
		});
	}
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 by CoNarrative
 */
glu.provider.itemsHelper = {

    /*
     * Handles additions to the observable list
     */
    respondToAdd:function (item, idx, context, needsDoLayout) {
        glu.log.indentMore();
        glu.log.debug(glu.log.indent + 'Processing a view item added to collection at index ' + idx);
        var list = context.viewmodel.get(context.binding.modelPropName);
        var container = context.control;
        if (!container.itemTemplate && !(item.mtype == 'viewmodel' || item.mtype == 'datamodel')) {
            throw "Cannot render an item within a bound items list because there is neither an itemTemplate defined nor is the item a viewmodel";
        }
        var viewItem = null;

        if (container.itemTemplate) {//use item template if it exists
            var viewItemSpec = glu.isFunction(container.itemTemplate) ? container.itemTemplate(item, idx) : glu.deepApply({}, container.itemTemplate);
            if (viewItemSpec === undefined)
                return;
            //do nothing if template is null...
            item.parentVM = list.parentVM;
            item.rootVM = list.rootVM;
            item.ns = list.ns;
            item.recType = list.recType;
            var result = glu.provider.binder.collectBindings(viewItemSpec, item, container.initialConfig);
            var boundConfigs = result.bindings;
            //Make a record observable as needed...
            if (item.phantom != null && boundConfigs.length > 0) {
                //TODO: Make sure that it isn't one way bindings...'
                this.makeRecordObservable(item);
            }
            viewItem = glu.widget(viewItemSpec);
            glu.provider.binder.applyBindingsList(boundConfigs);

        } else {//view model
            var viewModelName = item.viewmodelName;
            if( container.initialConfig.defaults && container.initialConfig.defaults.viewMode ){
                viewModelName += '_'+container.initialConfig.defaults.viewMode;
            }
            viewItem = glu.view(item, item.ns, viewModelName, {}, {}, container.initialConfig);
        }
        // if(viewItem.closable) {
        // interceptCloseCommand(viewItem);
        // }
        viewItem._vm = item; //add view model directly to view (for now)
        if (container.insert) {
            container.insert(idx, viewItem);
        } else {
            container.items.insert(idx, viewItem);
        }
        //apply as needed...
        if (needsDoLayout) {
            container.doLayout();
        }
        //No more doLayout in Touch
        if(Ext.getProvider().provider=='touch')
        {
            container.setActiveItem(0);
        }
        //make sure the view item has a reference back to the model in case it needs it...
        viewItem.model = item;
        glu.log.indentLess();
    },
    /* DOCS DISABLED FOR NOW
     * Initializes a bound item list
     * Does not deal with "activation"
     */
    bindItems:function (context, needsDoLayout) {
        glu.log.indentMore();
        glu.log.debug(glu.log.indent + 'processing bound items list');
        context.needsDoLayout = needsDoLayout;
        var list = context.viewmodel.get(context.binding.modelPropName);
        var container = context.control;
        context.valueSetTask = new Ext.util.DelayedTask(function () {
        });
        //MODEL -> CONTAINER
        //step through current items and create matching views
        var me = this;
        //support for enumerations across integer value
        //TODO: should use yield for newer javascripts...
        if (glu.isNumber(list)) {
            var count = list;
            list = [];
            for (var i = 0; i < count + 1; i++) {
                list.push(i);
            }
        }
        list.each = list.each || list.foreach ||
            function (process) {
                for (var i = 0; i < list.length; i++) {
                    process.call(me, list[i], i);
                }
            };

        list.each(function (item, idx) {
            this.respondToAdd(item, idx, context, false, true)
        }, this);
        if (needsDoLayout) {//just one time for all of those initial adds
            container.doLayout();
        }
        //if not observable, then a static list and stop listening...
        if (list.on === undefined)
            return;
        if (list._ob) {
            //its a glu list using the graph observable concept. This will clean up references on remove
            //listen to changed event on add/remove
            glu.temp = glu.temp || {};
            glu.temp.transfers = glu.temp.transfer || {};
            var attachPath = '_vm.' + context.binding.modelPropName +  '.';
            var transferKey = context.viewmodel.viewmodelName + '-' +context.binding.modelPropName;
            container._ob.on(attachPath + 'removedall', function(){
                //do a batch remove if possible. Later individual remove events will be ignored by the container
                if (container.removeAll) {
                    container.removeAll();
                }
            }, this);
            container._ob.on(attachPath + 'added', function (item, idx, isTransfer) {
                if (isTransfer) {
                    //re-use the transferred component
                    var transferral = glu.temp.transfers[transferKey];
                    var component = transferral.shift();
                    if (transferral.length==0) delete glu.temp.transfers[transferKey];
                    delete component._isTransferring;
                    container.autoDestroy = container._autoDestroy;
                    delete container._autoDestroy;
                    if (component.destroyed) {
                        //cannot reuse after all
                        this.respondToAdd(item, idx, context, needsDoLayout);
                        return;
                    }

                    if (container.insert) {
                        container.insert(idx, component);
                    } else {
                        container.items.insert(idx, component);
                    }
                    return;
                };
                this.respondToAdd(item, idx, context, needsDoLayout);
            }, this);
            container._ob.on(attachPath + 'removed', function (item, idx, isTransfer) {
                if (isTransfer) {
                    var component = container.items.getAt(idx);
                    component._isTransferring = true;
                    container._autoDestroy = container.autoDestroy;
                    container.autoDestroy = false;
                    //the key makes sure that we only re-use when moving between identical lists
                    glu.temp.transfers[transferKey] = glu.temp.transfers[transferKey] || [];
                    glu.temp.transfers[transferKey].push(component);
                }
                //suppress tab selection change events
                container._changeOriginatedFromModel=true;
                container.remove(idx);
                delete container._changeOriginatedFromModel;
            }, this);

        } else
        //if store, listen that way...
        if (list.data && list.data.on) {
            if (Ext.getVersion().major > 3 || Ext.getProvider().provider == 'touch') {
                //strange that 'add' does not work properly on store in Ext 4∆
                list.data.on('add', function (idx, item) {
                    this.respondToAdd(item, idx, context, needsDoLayout)
                }, this);
                list.data.on('remove', function (idx, item) {
                    //container._changeOriginatedFromModel=true;
                    container.remove(idx);
                }, this);
            } else {
                list.on('add', function (store, items, idx) {
                    for (var it = 0; it < items.length; it++) {
                        this.respondToAdd(items[it], idx + it, context, needsDoLayout)
                    }
                }, this);
                list.on('remove', function (store, item, idx) {
                    //suppress tab selection change events
                    container._changeOriginatedFromModel=true;
                    container.remove(idx);
                    delete container._changeOriginatedFromModel;
                }, this);
            }
        }
        glu.log.indentLess();

    },

    makeRecordObservable:function (item) {
        //TODO: Clean up and unify observable pattern as a mixin!!!!
        item.events = {};
        item.innerSet = item.set;
        item.registerControlBinding = function () {
        };
        //for testing
        item.on = function (name, callback, scope) {
            name = name.toLowerCase();
            if (!this.events.hasOwnProperty(name)) {
                this.events[name] = {
                    listeners:[]
                }
            }
            var evt = this.events[name];
            evt.listeners.push({
                fn:callback,
                scope:scope || glu
            });
        };
        item.fireEvent = function () {
            var name = arguments[0].toLowerCase();
            var args = Array.prototype.slice.call(arguments);
            args.shift();
            if (!this.events.hasOwnProperty(name)) {
                this.events[name] = {
                    listeners:[]
                }
            }
            var evt = this.events[name];
            for (var i = 0; i < evt.listeners.length; i++) {
                var listener = evt.listeners[i];
                var myVeto = listener.fn.apply(listener.scope, args);
                if (myVeto === true) {
                    return false;
                }
            }
            return true;
        };
        item.set = function (propName, value) {
            var oldValue = this.get(propName);
            if (oldValue === value) {
                return;
                //do nothing if it's the same thing.
            }
            this.innerSet(propName, value);
            this.fireEvent(propName + 'Changed', value, oldValue, {
                modelPropName:propName
            });
            this.fireEvent('changed', value, oldValue, {
                modelPropName:propName
            });

        }
    }
};
<commit_msg>Added async layouts - when configured with glu.asyncLayouts it suspends Ext layouts until the UI thread exits. Results in large performance improvements. Possible because for us a view is just window dressing and doesn't need to be synchronous.<commit_after>/*
 * Copyright (C) 2012 by CoNarrative
 */
glu.provider.itemsHelper = {

    /*
     * Handles additions to the observable list
     */
    respondToAdd:function (item, idx, context, needsDoLayout) {
        glu.log.indentMore();
        glu.log.debug(glu.log.indent + 'Processing a view item added to collection at index ' + idx);
        var list = context.viewmodel.get(context.binding.modelPropName);
        var container = context.control;
        if (!container.itemTemplate && !(item.mtype == 'viewmodel' || item.mtype == 'datamodel')) {
            throw "Cannot render an item within a bound items list because there is neither an itemTemplate defined nor is the item a viewmodel";
        }
        var viewItem = null;

        if (container.itemTemplate) {//use item template if it exists
            var viewItemSpec = glu.isFunction(container.itemTemplate) ? container.itemTemplate(item, idx) : glu.deepApply({}, container.itemTemplate);
            if (viewItemSpec === undefined)
                return;
            //do nothing if template is null...
            item.parentVM = list.parentVM;
            item.rootVM = list.rootVM;
            item.ns = list.ns;
            item.recType = list.recType;
            var result = glu.provider.binder.collectBindings(viewItemSpec, item, container.initialConfig);
            var boundConfigs = result.bindings;
            //Make a record observable as needed...
            if (item.phantom != null && boundConfigs.length > 0) {
                //TODO: Make sure that it isn't one way bindings...'
                this.makeRecordObservable(item);
            }
            viewItem = glu.widget(viewItemSpec);
            glu.provider.binder.applyBindingsList(boundConfigs);

        } else {//view model
            var viewModelName = item.viewmodelName;
            if( container.initialConfig.defaults && container.initialConfig.defaults.viewMode ){
                viewModelName += '_'+container.initialConfig.defaults.viewMode;
            }
            viewItem = glu.view(item, item.ns, viewModelName, {}, {}, container.initialConfig);
        }
        // if(viewItem.closable) {
        // interceptCloseCommand(viewItem);
        // }
        viewItem._vm = item; //add view model directly to view (for now)
        if (container.insert) {
            container.insert(idx, viewItem);
        } else {
            container.items.insert(idx, viewItem);
        }
        //apply as needed...
        if (needsDoLayout) {
            container.doLayout();
        }
        //No more doLayout in Touch
        if(Ext.getProvider().provider=='touch')
        {
            container.setActiveItem(0);
        }
        //make sure the view item has a reference back to the model in case it needs it...
        viewItem.model = item;
        glu.log.indentLess();
    },

    deferredLayoutTask: new Ext.util.DelayedTask(function(){
        glu._suspendingLayout = false;
        Ext.resumeLayouts(true);
    }),

    deferLayoutsIfNeeded : function(){
        if (!glu.asyncLayouts || !Ext.suspendLayouts) return;
        if (!glu._suspendingLayout) {
            Ext.suspendLayouts();
            glu._suspendingLayout = true;
        }
        this.deferredLayoutTask.delay(1); //go as soon as the thread is done
    },

    /* DOCS DISABLED FOR NOW
     * Initializes a bound item list
     * Does not deal with "activation"
     */
    bindItems:function (context, needsDoLayout) {
        glu.log.indentMore();
        glu.log.debug(glu.log.indent + 'processing bound items list');
        context.needsDoLayout = needsDoLayout;
        var list = context.viewmodel.get(context.binding.modelPropName);
        var container = context.control;
        context.valueSetTask = new Ext.util.DelayedTask(function () {
        });
        //MODEL -> CONTAINER
        //step through current items and create matching views
        var me = this;
        //support for enumerations across integer value
        //TODO: should use yield for newer javascripts...
        if (glu.isNumber(list)) {
            var count = list;
            list = [];
            for (var i = 0; i < count + 1; i++) {
                list.push(i);
            }
        }
        list.each = list.each || list.foreach ||
            function (process) {
                for (var i = 0; i < list.length; i++) {
                    process.call(me, list[i], i);
                }
            };

        list.each(function (item, idx) {
            this.respondToAdd(item, idx, context, false, true)
        }, this);
        if (needsDoLayout) {//just one time for all of those initial adds
            container.doLayout();
        }
        //if not observable, then a static list and stop listening...
        if (list.on === undefined)
            return;

        if (list._ob) {
            //its a glu list using the graph observable concept. This will clean up references on remove
            //listen to changed event on add/remove
            glu.temp = glu.temp || {};
            glu.temp.transfers = glu.temp.transfer || {};
            var attachPath = '_vm.' + context.binding.modelPropName +  '.';
            var transferKey = context.viewmodel.viewmodelName + '-' +context.binding.modelPropName;
            container._ob.on(attachPath + 'removedall', function(){
                //do a batch remove if possible. Later individual remove events will be ignored by the container
                if (container.removeAll) {
                    this.deferLayoutsIfNeeded();
                    container.removeAll();
                }
            }, this);
            container._ob.on(attachPath + 'added', function (item, idx, isTransfer) {
                if (isTransfer) {
                    //re-use the transferred component
                    var transferral = glu.temp.transfers[transferKey];
                    var component = transferral.shift();
                    if (transferral.length==0) delete glu.temp.transfers[transferKey];
                    delete component._isTransferring;
                    container.autoDestroy = container._autoDestroy;
                    delete container._autoDestroy;
                    if (component.destroyed) {
                        //cannot reuse after all
                        this.deferLayoutsIfNeeded();
                        this.respondToAdd(item, idx, context, needsDoLayout);
                        return;
                    }

                    if (container.insert) {
                        this.deferLayoutsIfNeeded();
                        container.insert(idx, component);
                    } else {
                        this.deferLayoutsIfNeeded();
                        container.items.insert(idx, component);
                    }
                    return;
                };
                this.respondToAdd(item, idx, context, needsDoLayout);
            }, this);
            container._ob.on(attachPath + 'removed', function (item, idx, isTransfer) {
                if (isTransfer) {
                    var component = container.items.getAt(idx);
                    component._isTransferring = true;
                    container._autoDestroy = container.autoDestroy;
                    container.autoDestroy = false;
                    //the key makes sure that we only re-use when moving between identical lists
                    glu.temp.transfers[transferKey] = glu.temp.transfers[transferKey] || [];
                    glu.temp.transfers[transferKey].push(component);
                }
                //suppress tab selection change events
                container._changeOriginatedFromModel=true;
                container.remove(idx);
                delete container._changeOriginatedFromModel;
            }, this);

        } else
        //if store, listen that way...
        if (list.data && list.data.on) {
            if (Ext.getVersion().major > 3 || Ext.getProvider().provider == 'touch') {
                //strange that 'add' does not work properly on store in Ext 4∆
                list.data.on('add', function (idx, item) {
                    this.respondToAdd(item, idx, context, needsDoLayout)
                }, this);
                list.data.on('remove', function (idx, item) {
                    //container._changeOriginatedFromModel=true;
                    container.remove(idx);
                }, this);
            } else {
                list.on('add', function (store, items, idx) {
                    for (var it = 0; it < items.length; it++) {
                        this.respondToAdd(items[it], idx + it, context, needsDoLayout)
                    }
                }, this);
                list.on('remove', function (store, item, idx) {
                    //suppress tab selection change events
                    container._changeOriginatedFromModel=true;
                    container.remove(idx);
                    delete container._changeOriginatedFromModel;
                }, this);
            }
        }
        glu.log.indentLess();

    },

    makeRecordObservable:function (item) {
        //TODO: Clean up and unify observable pattern as a mixin!!!!
        item.events = {};
        item.innerSet = item.set;
        item.registerControlBinding = function () {
        };
        //for testing
        item.on = function (name, callback, scope) {
            name = name.toLowerCase();
            if (!this.events.hasOwnProperty(name)) {
                this.events[name] = {
                    listeners:[]
                }
            }
            var evt = this.events[name];
            evt.listeners.push({
                fn:callback,
                scope:scope || glu
            });
        };
        item.fireEvent = function () {
            var name = arguments[0].toLowerCase();
            var args = Array.prototype.slice.call(arguments);
            args.shift();
            if (!this.events.hasOwnProperty(name)) {
                this.events[name] = {
                    listeners:[]
                }
            }
            var evt = this.events[name];
            for (var i = 0; i < evt.listeners.length; i++) {
                var listener = evt.listeners[i];
                var myVeto = listener.fn.apply(listener.scope, args);
                if (myVeto === true) {
                    return false;
                }
            }
            return true;
        };
        item.set = function (propName, value) {
            var oldValue = this.get(propName);
            if (oldValue === value) {
                return;
                //do nothing if it's the same thing.
            }
            this.innerSet(propName, value);
            this.fireEvent(propName + 'Changed', value, oldValue, {
                modelPropName:propName
            });
            this.fireEvent('changed', value, oldValue, {
                modelPropName:propName
            });

        }
    }
};
<|endoftext|>


<commit_before>angular.module('blocktrail.wallet')
    .service( 'Currencies', function($rootScope, storageService, $log, sdkService, CONFIG) {
        var self = this;

        self.cache = storageService.db('currency-rates-cache');

        // currencies that the app supports and their symbols
        //  this list shouldn't be used directly `self.currencies` contains the enabled currencies
        var _currencies = {
            BTC: {code: "BTC", ticker: $rootScope.TICKER, symbol: "฿"},
            GBP: {code: "GBP", symbol: "£"},
            EUR: {code: "EUR", symbol: "€"},
            USD: {code: "USD", symbol: "$"},
            CNY: {code: "CNY", symbol: "¥"},
            PAB: {code: "PAB", symbol: "B/"},
            ARS: {code: "ARS", symbol: "$"},
            BOB: {code: "BOB", symbol: "$b"},
            CLP: {code: "CLP", symbol: "$"},
            PEN: {code: "PEN", symbol: "S/"},
            PYG: {code: "PYG", symbol: "Gs"},
            UYU: {code: "UYU", symbol: "$U"},
            VEF: {code: "VEF", symbol: "Bs"},
            CRC: {code: "CRC", symbol: "₡"},
            MXN: {code: "MXN", symbol: "$"},
            NGN: {code: "NGN", symbol: "₦"},
            INR: {code: "INR", symbol: "₹"},
            RUB: {code: "RUB", symbol: "₽"}
        };

        self.currencies = {};

        self.getCurrencies = function() {
            var self = this;
            return Object.keys(self.currencies).map(function(code) {
                return self.currencies[code];
            });
        };

        self.getFiatCurrencies = function() {
            var self = this;
            return self.getCurrencies().filter(function(currency) { return !!currency.isFiat; });
        };

        self.price = function(currency) {
            var self = this;
            if (typeof self.currencies[currency] === "undefined") {
                return 0;
            } else {
                return self.currencies[currency].price || 0;
            }
        };

        self.prices = function() {
            var prices = {};

            Object.keys(self.currencies).forEach(function(code) {
                prices[code] = self.currencies[code].price;
            });

            return prices;
        };

        /**
         * get the current btc prices (defaults to live, can force getting a cached version)
         * @param getCached
         * @returns {*}
         */
        var updatePrices = null;
        self.updatePrices = function(getCached) {
            var self = this;

            var forceFetch = !getCached;

            if (updatePrices) {
                if (updatePrices.forceFetch !== forceFetch) {
                    return updatePrices.then(function() {
                        return self.updatePrices(getCached);
                    });
                } else {
                    return updatePrices;
                }
            } else {
                updatePrices = self.cache.get('price')
                    .then(function(b) {
                        return b;
                    }, function() {
                        forceFetch = true;
                        return {_id: "price"};
                    })
                    .then(function(pricesDoc) {
                        if (forceFetch) {
                            return sdkService.sdk().then(function(sdk) {
                                return sdk.price().then(function(result) {
                                    angular.extend(pricesDoc, result);

                                    //store in cache and then return
                                    return self.cache.put(pricesDoc).then(function() {
                                        return pricesDoc;
                                    });
                                });
                            });
                        } else {
                            return pricesDoc;
                        }
                    })
                    // use a .then because a .done would break the promise chains that rely on self.wallet
                    .then(function(pricesDoc) {
                        Object.keys(self.currencies).forEach(function(code) {
                            self.currencies[code].price = parseFloat(pricesDoc[code] || 0);
                        });
                        return pricesDoc;
                    }, function(e) {
                        $log.error('prices ERR ' + e);
                        throw e;
                    })
                    .then(function(r) {
                        updatePrices = null;
                        return r;
                    });

                // keep track if we forced for this
                updatePrices.forceFetch = forceFetch;

                return updatePrices;
            }
        };

        self.enableCurrency = function(code) {
            var self = this;

            if (typeof _currencies[code] === "undefined") {
                return false;
            }

            self.currencies[code] = _currencies[code];
            self.currencies[code].isFiat = self.currencies[code].code !== "BTC";
            self.currencies[code].btcRate = 0;

            return true;
        };

        // enable all currencies that are in the config
        CONFIG.CURRENCIES.forEach(function(code) {
            self.enableCurrency(code);
        });
    })
    .service('CurrencyConverter', function($rootScope, Currencies) {
        var self = this;
        var coin = 100000000;
        var precision = 8;

        /**
         * convert from BTC value to a given currency
         * @param value
         * @param currency
         * @param fractionSize (optional)
         */
        self.fromBTC = function(value, currency, fractionSize) {
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 2;
            }

            return Currencies.price(currency) ? (parseFloat(value) * Currencies.price(currency)).toFixed(fractionSize) : (0).toFixed(fractionSize);
        };

        /**
         * converts from a given currency value to BTC
         * @param value
         * @param currency
         * @param fractionSize (optional)
         */
        self.toBTC = function(value, currency, fractionSize) {
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 8;
            }

            return Currencies.price(currency) ? (parseFloat(value) / Currencies.price(currency)).toFixed(fractionSize) : (0).toFixed(fractionSize);
        };

        /**
         * convert from Satoshi value to a given currency
         * @param value
         * @param currency
         * @param fractionSize (optional)
         */
        self.fromSatoshi = function(value, currency, fractionSize) {
            var btcValue = parseFloat((value/ coin).toFixed(precision));
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 2;
            }

            return Currencies.price(currency) ? (btcValue * Currencies.price(currency)).toFixed(fractionSize) : (0).toFixed(fractionSize);
        };

        /**
         * converts from a given currency value to Satoshi
         * @param value
         * @param currency
         */
        self.toSatoshi = function(value, currency) {
            var btcValue;
            if (currency == "BTC") {
                btcValue = parseFloat(value);
            } else {
                btcValue = self.toBTC(value, currency, precision);
            }

            return parseFloat((btcValue * coin).toFixed(0));
        };
    })
    .filter('currencySymbol', function(Currencies) {
        return function(input) {
            return Currencies.currencies[input].symbol;
        }
    });
<commit_msg>moar fiat<commit_after>angular.module('blocktrail.wallet')
    .service( 'Currencies', function($rootScope, storageService, $log, sdkService, CONFIG) {
        var self = this;

        self.cache = storageService.db('currency-rates-cache');

        // currencies that the app supports and their symbols
        //  this list shouldn't be used directly `self.currencies` contains the enabled currencies
        var _currencies = {
            BTC: {code: "BTC", ticker: $rootScope.TICKER, symbol: "฿"},
            GBP: {code: "GBP", symbol: "£"},
            EUR: {code: "EUR", symbol: "€"},
            USD: {code: "USD", symbol: "$"},
            CNY: {code: "CNY", symbol: "¥"},
            PAB: {code: "PAB", symbol: "B/"},
            ARS: {code: "ARS", symbol: "$"},
            BOB: {code: "BOB", symbol: "$b"},
            CLP: {code: "CLP", symbol: "$"},
            PEN: {code: "PEN", symbol: "S/"},
            PYG: {code: "PYG", symbol: "Gs"},
            UYU: {code: "UYU", symbol: "$U"},
            VEF: {code: "VEF", symbol: "Bs"},
            CRC: {code: "CRC", symbol: "₡"},
            MXN: {code: "MXN", symbol: "$"},
            NGN: {code: "NGN", symbol: "₦"},
            INR: {code: "INR", symbol: "₹"},
            RUB: {code: "RUB", symbol: "₽"},
            AUD: {code: "AUD", symbol: "$"},
            BRL: {code: "BRL", symbol: "R$"},
            ZAR: {code: "ZAR", symbol: "R"},
            ILS: {code: "ILS", symbol: "₪"},
            SAR: {code: "SAR", symbol: "﷼"},
            JPY: {code: "JPY", symbol: "¥"},
            KRW: {code: "KRW", symbol: "₩"},
            COP: {code: "COP", symbol: "$"},
            GHS: {code: "GHS", symbol: "GH¢"}
        };

        self.currencies = {};

        self.getCurrencies = function() {
            var self = this;
            return Object.keys(self.currencies).map(function(code) {
                return self.currencies[code];
            });
        };

        self.getFiatCurrencies = function() {
            var self = this;
            return self.getCurrencies().filter(function(currency) { return !!currency.isFiat; });
        };

        self.price = function(currency) {
            var self = this;
            if (typeof self.currencies[currency] === "undefined") {
                return 0;
            } else {
                return self.currencies[currency].price || 0;
            }
        };

        self.prices = function() {
            var prices = {};

            Object.keys(self.currencies).forEach(function(code) {
                prices[code] = self.currencies[code].price;
            });

            return prices;
        };

        /**
         * get the current btc prices (defaults to live, can force getting a cached version)
         * @param getCached
         * @returns {*}
         */
        var updatePrices = null;
        self.updatePrices = function(getCached) {
            var self = this;

            var forceFetch = !getCached;

            if (updatePrices) {
                if (updatePrices.forceFetch !== forceFetch) {
                    return updatePrices.then(function() {
                        return self.updatePrices(getCached);
                    });
                } else {
                    return updatePrices;
                }
            } else {
                updatePrices = self.cache.get('price')
                    .then(function(b) {
                        return b;
                    }, function() {
                        forceFetch = true;
                        return {_id: "price"};
                    })
                    .then(function(pricesDoc) {
                        if (forceFetch) {
                            return sdkService.sdk().then(function(sdk) {
                                return sdk.price().then(function(result) {
                                    angular.extend(pricesDoc, result);

                                    //store in cache and then return
                                    return self.cache.put(pricesDoc).then(function() {
                                        return pricesDoc;
                                    });
                                });
                            });
                        } else {
                            return pricesDoc;
                        }
                    })
                    // use a .then because a .done would break the promise chains that rely on self.wallet
                    .then(function(pricesDoc) {
                        Object.keys(self.currencies).forEach(function(code) {
                            self.currencies[code].price = parseFloat(pricesDoc[code] || 0);
                        });
                        return pricesDoc;
                    }, function(e) {
                        $log.error('prices ERR ' + e);
                        throw e;
                    })
                    .then(function(r) {
                        updatePrices = null;
                        return r;
                    });

                // keep track if we forced for this
                updatePrices.forceFetch = forceFetch;

                return updatePrices;
            }
        };

        self.enableCurrency = function(code) {
            var self = this;

            if (typeof _currencies[code] === "undefined") {
                return false;
            }

            self.currencies[code] = _currencies[code];
            self.currencies[code].isFiat = self.currencies[code].code !== "BTC";
            self.currencies[code].btcRate = 0;

            return true;
        };

        // enable all currencies that are in the config
        CONFIG.CURRENCIES.forEach(function(code) {
            self.enableCurrency(code);
        });
    })
    .service('CurrencyConverter', function($rootScope, Currencies) {
        var self = this;
        var coin = 100000000;
        var precision = 8;

        /**
         * convert from BTC value to a given currency
         * @param value
         * @param currency
         * @param fractionSize (optional)
         */
        self.fromBTC = function(value, currency, fractionSize) {
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 2;
            }

            return Currencies.price(currency) ? (parseFloat(value) * Currencies.price(currency)).toFixed(fractionSize) : (0).toFixed(fractionSize);
        };

        /**
         * converts from a given currency value to BTC
         * @param value
         * @param currency
         * @param fractionSize (optional)
         */
        self.toBTC = function(value, currency, fractionSize) {
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 8;
            }

            return Currencies.price(currency) ? (parseFloat(value) / Currencies.price(currency)).toFixed(fractionSize) : (0).toFixed(fractionSize);
        };

        /**
         * convert from Satoshi value to a given currency
         * @param value
         * @param currency
         * @param fractionSize (optional)
         */
        self.fromSatoshi = function(value, currency, fractionSize) {
            var btcValue = parseFloat((value/ coin).toFixed(precision));
            if (typeof(fractionSize) == "undefined") {
                fractionSize = 2;
            }

            return Currencies.price(currency) ? (btcValue * Currencies.price(currency)).toFixed(fractionSize) : (0).toFixed(fractionSize);
        };

        /**
         * converts from a given currency value to Satoshi
         * @param value
         * @param currency
         */
        self.toSatoshi = function(value, currency) {
            var btcValue;
            if (currency == "BTC") {
                btcValue = parseFloat(value);
            } else {
                btcValue = self.toBTC(value, currency, precision);
            }

            return parseFloat((btcValue * coin).toFixed(0));
        };
    })
    .filter('currencySymbol', function(Currencies) {
        return function(input) {
            return Currencies.currencies[input].symbol;
        }
    });
<|endoftext|>


<commit_before>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global X:true, XM:true, issue:true, XT:true */

(function () {
  "use strict";

  var _ = X._;
  //var _util = X.util;

  /**
    Session object for datasource.

    @class
    @extends X.Object
   */
  X.Session = X.Object.extend(/** @lends X.Session.prototype */{

    isReady: false,
    isLoaded: false,

    init: function () {
      var id = this.get("id"), sid = this.get("sid"),
          selected = this.get("selected"), logout = this.get("logout");

      if (logout) {
        this.load(_.bind(this.didLoadForLogout, this));
      } else if (!sid) {
        X.Session.init.call(this);
        process.nextTick(_.bind(function () { this.set("isReady", true); }, this));
        this.commit();
      } else if (selected && id && sid) {

        //X.debugging = true;
        //X.debug("ABOUT TO SELECT: ", id, selected, sid);

        this.load(_.bind(this.didLoadForSelection, this));
      } else {

        //X.debugging = true;
        //X.debug("ABOUT LOAD: ", id, sid, selected);

        this.load();
      }
    },

    //.....................................
    // ACTION METHODS
    //
    /**
      Commit.

     */
    commit: function () {
      //X.log("commit(): ", _util.inspect(this));

      var K = this.get("model"),
        k = new K(),
        that = this,
        options = {};

      if (this.get("isLoaded")) {
        // we were loaded so a commit needs to update
        // not create a new session entry
        options.success = function (res) { that.updateForCommit(null, k); };
        options.error = function (model, err) { that.updateForCommit(err); };
        options.id = this.get("sid");
        return k.fetch(options);
      }

      options.success = function () { that.didCommit(); };
      k.initialize(null, {isNew: true});
      k.save(this.get("details"), options);
    },
    /**
      Update for commit

     */
    updateForCommit: function (err, res) {
      //X.log("updateForCommit(): ");

      var details = this.get("details"),
        that = this,
        options = {},
        key;

      if (err || !res) {
        return this.set("error", "could not update");
      }

      for (key in details) {
        if (!details.hasOwnProperty(key)) continue;
        res.set(key, details[key]);
      }

      options.success = function () { that.didCommit(); };
      options.error = function (model, err) { that.didCommit(err); };
      res.save(null, options);
    },

    //.....................................
    // POST-ACTION METHODS
    //

    didLoad: function (err, res) {
      //X.log("didLoad(): ", err, res);
      var loaded,
        prop;

      if (err) {
        return this.set("error", err.message, {stack: err.stack});
      } else if (!res) {
        // something is wrong, we could not find the session that
        // was requested
        return this.set("error", "could not retrieve session", {code: "SESSION_NOT_FOUND"});
      }

      loaded = res.toJSON();

      // Need to remove null values... no, it doesn't make a lot of sense
      for (prop in loaded) {
        if (loaded.hasOwnProperty(prop)) {
          if (_.isNull(loaded[prop])) {
            delete loaded[prop];
          }
        }
      }

      X.mixin(this, loaded);

      this.set("isLoaded", true);

      this.created = res.get('created');
      this.checksum = res.get('checksum');
      this.lastModified = X.Session.timestamp();
      this.set("isReady", true);

      //this.isReadyDidChange();

      // commit update?
      process.nextTick(_.bind(this.commit, this));
    },

    didCommit: function (err) {
      //X.log("didCommit(): ", err);

      if (err) {
        this.set("error", err.message(), err.code);
      } else {
        // TODO: this _can_ be done lazily right?
        // this.set("isReady", true);
        this.emit("didCommit");
      }
    },

    didLoadForSelection: function (err, res) {
      //X.debug("didLoadForSelection(): ", res);
      var organizations = this.get("organizations"), selected, found;
      if (!organizations || organizations.length <= 0) {
        return this.set("error", "no organizations available for selection");
      } else if (err) { return this.didLoad(err, res); }
      selected = this.get("selected");
      if ((found = _.find(organizations, function (o) {
        return o.name === selected ? true: false;
      }))) {
        // TODO: this needs to seriously be reevaluated
        // as it seems like if for any reason the session
        // already had an organization listed (somehow)
        // it will be overwritten and cause unknown
        // behaviors as a result
        this.set("organization", selected);
        this.set("username", found.username);
        this.didLoad(null, res);
      } else this.set("error", "invalid organization selected for user");
    },

    didLoadForLogout: function (err, res) {
      if (err) {
        return this.set("error", "could not load the session to logout");
      }
      // successfully loaded the session which means its valid
      // lets go ahead and logout by destroying ourselves
      this.set("isLoaded", true);
      this.destroy(res);
    },

    destroy: function (session) {
      if (session && session.destroy) { session.destroy(); }
      this.set("isReady", true);
    },

    query: function (query, callback) {
      var options,
        payload = this.payload,
        organization = this.get("organization"), orig = callback;
      if (!this.get("isLoaded")) {
        X.warn("Error: cannot query unloaded session");
        return this.set("error", "cannot query unloaded session");
      }
      callback = _.bind(this.didQuery, this, orig);
      //X.debug("query: %@".f(query));
      if (payload && payload.databaseType === 'global') {
        // Gangnam style authentication
        var authorizedUsers = ["admin", "jrogelstad", "shackbarth"];
        if (authorizedUsers.indexOf(this.username) < 0 &&
            (payload.requestType !== 'dispatch' || payload.functionName !== 'schema')) {
          //this.set("error", "unauthorized access");
          // not sure why that crashes the dataserver
          return;
        }
        // run this query against the global database
        options = JSON.parse(JSON.stringify(this.payload)); // clone
        options.success = function (resp) { callback(null, resp); };
        options.error = function (model, err) { callback(err); };
        if (payload.requestType === 'fetch') {
          XT.dataSource.fetch(options);
        } else if (payload.requestType === 'dispatch') {
          XT.dataSource.dispatch(payload.className, payload.functionName, payload.parameters, options);
        } else if (payload.requestType === 'retrieveRecord') {
          XT.dataSource.retrieveRecord(payload.recordType, payload.id, options);
        } else if (payload.requestType === 'commitRecord') {
          if (!payload.dataHash) { return this.set("error", "invalid commit"); }
          // Passing payload through, but trick dataSource into thinking it's a Model:
          payload.changeSet = function () { return payload.dataHash; };
          options.force = true;
          XT.dataSource.commitRecord(payload, options);
        }
      } else {
        // run this query against an instance database
        X.database.query(organization, query, callback);
      }
      return this;
    },

    didQuery: function (callback, err, res) {
      if (err) return callback(err);
      if (res || res === 0) {
        callback(null, res);
      } else {
        this.set("error", "no result from query");
      }
    },

    //.....................................
    // PROPERTY METHODS
    //

    model: function () {
      return XM.Session;
    }.property(),

    details: function () {
      var d = {
        id: this.get("id"),
        sid: this.get("sid"),
        lastModified: this.get("lastModified") || X.Session.timestamp(),
        created: this.get("created"),
        checksum: "",
        username: this.get("username"),
        organization: this.get("organization"),
        organizations: this.get("organizations"),
        socket: this.get("socket")
      };

      //X.debug("details", d);

      return d;
    }.property(),

    socket: function (socket) {
      if (arguments.length === 0) return this._socketId;
      this._socketId = socket.id;
      this.commit();
    }.property(),

    error: function () {
      if (arguments.length === 0) return this._error;
      var err = this._error = {}, args = X.$A(arguments);
      err.reason = args.shift();
      while (args.length > 0) {
        X.mixin(err, args.shift());
      }
    }.property(),

    //.....................................
    // PROXIED METHODS
    //

    load: function () {
      return X.Session.load.apply(this, arguments);
    }
  });

  X.mixin(X.Session, /** @lends X.Session */{

    cache: null,

    load: function (callback) {
      //X.log("load(): ");

      var K = this.get("model"),
        k = new K(),
        options = {};

      if (!callback) { callback = _.bind(this.didLoad, this); }
      options.id = this.get("sid") || -1;
      options.success = function (resp) { callback(null, resp); };
      options.error = function (model, err) { callback(err); };

      //X.log("load(): ", details);

      k.fetch(options);
      return this;
    },

    checksum: function () {
      //var hash = X.Session.sha1hash(), data = ;
      //data = X.typeOf(data) === X.T_HASH ? X.json(data) : data;
      //return hash.update(data).digest('hex');
    },

    init: function () {
      this.sid = X.Session.generateSID();
      this.created = X.Session.timestamp();
    },

    generateSID: function () {
      // http://www.ietf.org/rfc/rfc4122.txt
      var s = [];
      var hexDigits = "0123456789abcdef";
      for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
      }
      s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
      s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
      s[8] = s[13] = s[18] = s[23] = "-";

      var uuid = s.join("");
      return uuid;
    },

    timestamp: function () { return new Date().getTime(); },

    sha1hash: function () { return X.crypto.createHash('sha1'); },

    md5hash: function () { return X.crypto.createHash('md5'); },

    /**
      Polls database and logs users out upon session timeout. This method
      can be used to do any actions that are driven from the cache.
     */
    pollCache: function () {
      var sessions = new XM.SessionCollection(),
        i,
        diff,
        ok,
        now,
        model,
        socket,
        sockets,
        options = {};

      sockets = X.Server.getServer("Data").socketsFor("/clientsock");

      options.success = function () {
        ok = (X.options.datasource.sessionTimeout || 15) * 60000;
        now = X.Session.timestamp();

        //console.log("RUNNING", ok, now);

        if (sessions && sessions.length > 0) {
          for (i = 0; i < sessions.length; ++i) {
            model = sessions.at(i);
            diff = Math.abs(now - (model.get('lastModified') || 1000000000));
            if (diff > ok) {
              socket = sockets[model.get('socket')];
              if (socket) {
                socket.emit("timeout");
                socket.disconnect();
              }
              model.destroy();
            }
          }
        }
      };
      options.error = function (model, err) {
        // we have serious issues if we have an error here!
        return issue(X.fatal("error polling cache", err.message(), err.code));
      };
      sessions.fetch(options);
    },

    isActiveSession: function () {
    }
  });

}());
<commit_msg>issue #18649: Let ANY useful result pass on through<commit_after>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global X:true, XM:true, issue:true, XT:true */

(function () {
  "use strict";

  var _ = X._;
  //var _util = X.util;

  /**
    Session object for datasource.

    @class
    @extends X.Object
   */
  X.Session = X.Object.extend(/** @lends X.Session.prototype */{

    isReady: false,
    isLoaded: false,

    init: function () {
      var id = this.get("id"), sid = this.get("sid"),
          selected = this.get("selected"), logout = this.get("logout");

      if (logout) {
        this.load(_.bind(this.didLoadForLogout, this));
      } else if (!sid) {
        X.Session.init.call(this);
        process.nextTick(_.bind(function () { this.set("isReady", true); }, this));
        this.commit();
      } else if (selected && id && sid) {

        //X.debugging = true;
        //X.debug("ABOUT TO SELECT: ", id, selected, sid);

        this.load(_.bind(this.didLoadForSelection, this));
      } else {

        //X.debugging = true;
        //X.debug("ABOUT LOAD: ", id, sid, selected);

        this.load();
      }
    },

    //.....................................
    // ACTION METHODS
    //
    /**
      Commit.

     */
    commit: function () {
      //X.log("commit(): ", _util.inspect(this));

      var K = this.get("model"),
        k = new K(),
        that = this,
        options = {};

      if (this.get("isLoaded")) {
        // we were loaded so a commit needs to update
        // not create a new session entry
        options.success = function (res) { that.updateForCommit(null, k); };
        options.error = function (model, err) { that.updateForCommit(err); };
        options.id = this.get("sid");
        return k.fetch(options);
      }

      options.success = function () { that.didCommit(); };
      k.initialize(null, {isNew: true});
      k.save(this.get("details"), options);
    },
    /**
      Update for commit

     */
    updateForCommit: function (err, res) {
      //X.log("updateForCommit(): ");

      var details = this.get("details"),
        that = this,
        options = {},
        key;

      if (err || !res) {
        return this.set("error", "could not update");
      }

      for (key in details) {
        if (!details.hasOwnProperty(key)) continue;
        res.set(key, details[key]);
      }

      options.success = function () { that.didCommit(); };
      options.error = function (model, err) { that.didCommit(err); };
      res.save(null, options);
    },

    //.....................................
    // POST-ACTION METHODS
    //

    didLoad: function (err, res) {
      //X.log("didLoad(): ", err, res);
      var loaded,
        prop;

      if (err) {
        return this.set("error", err.message, {stack: err.stack});
      } else if (!res) {
        // something is wrong, we could not find the session that
        // was requested
        return this.set("error", "could not retrieve session", {code: "SESSION_NOT_FOUND"});
      }

      loaded = res.toJSON();

      // Need to remove null values... no, it doesn't make a lot of sense
      for (prop in loaded) {
        if (loaded.hasOwnProperty(prop)) {
          if (_.isNull(loaded[prop])) {
            delete loaded[prop];
          }
        }
      }

      X.mixin(this, loaded);

      this.set("isLoaded", true);

      this.created = res.get('created');
      this.checksum = res.get('checksum');
      this.lastModified = X.Session.timestamp();
      this.set("isReady", true);

      //this.isReadyDidChange();

      // commit update?
      process.nextTick(_.bind(this.commit, this));
    },

    didCommit: function (err) {
      //X.log("didCommit(): ", err);

      if (err) {
        this.set("error", err.message(), err.code);
      } else {
        // TODO: this _can_ be done lazily right?
        // this.set("isReady", true);
        this.emit("didCommit");
      }
    },

    didLoadForSelection: function (err, res) {
      //X.debug("didLoadForSelection(): ", res);
      var organizations = this.get("organizations"), selected, found;
      if (!organizations || organizations.length <= 0) {
        return this.set("error", "no organizations available for selection");
      } else if (err) { return this.didLoad(err, res); }
      selected = this.get("selected");
      if ((found = _.find(organizations, function (o) {
        return o.name === selected ? true: false;
      }))) {
        // TODO: this needs to seriously be reevaluated
        // as it seems like if for any reason the session
        // already had an organization listed (somehow)
        // it will be overwritten and cause unknown
        // behaviors as a result
        this.set("organization", selected);
        this.set("username", found.username);
        this.didLoad(null, res);
      } else this.set("error", "invalid organization selected for user");
    },

    didLoadForLogout: function (err, res) {
      if (err) {
        return this.set("error", "could not load the session to logout");
      }
      // successfully loaded the session which means its valid
      // lets go ahead and logout by destroying ourselves
      this.set("isLoaded", true);
      this.destroy(res);
    },

    destroy: function (session) {
      if (session && session.destroy) { session.destroy(); }
      this.set("isReady", true);
    },

    query: function (query, callback) {
      var options,
        payload = this.payload,
        organization = this.get("organization"), orig = callback;
      if (!this.get("isLoaded")) {
        X.warn("Error: cannot query unloaded session");
        return this.set("error", "cannot query unloaded session");
      }
      callback = _.bind(this.didQuery, this, orig);
      //X.debug("query: %@".f(query));
      if (payload && payload.databaseType === 'global') {
        // Gangnam style authentication
        var authorizedUsers = ["admin", "jrogelstad", "shackbarth"];
        if (authorizedUsers.indexOf(this.username) < 0 &&
            (payload.requestType !== 'dispatch' || payload.functionName !== 'schema')) {
          //this.set("error", "unauthorized access");
          // not sure why that crashes the dataserver
          return;
        }
        // run this query against the global database
        options = JSON.parse(JSON.stringify(this.payload)); // clone
        options.success = function (resp) { callback(null, resp); };
        options.error = function (model, err) { callback(err); };
        if (payload.requestType === 'fetch') {
          XT.dataSource.fetch(options);
        } else if (payload.requestType === 'dispatch') {
          XT.dataSource.dispatch(payload.className, payload.functionName, payload.parameters, options);
        } else if (payload.requestType === 'retrieveRecord') {
          XT.dataSource.retrieveRecord(payload.recordType, payload.id, options);
        } else if (payload.requestType === 'commitRecord') {
          if (!payload.dataHash) { return this.set("error", "invalid commit"); }
          // Passing payload through, but trick dataSource into thinking it's a Model:
          payload.changeSet = function () { return payload.dataHash; };
          options.force = true;
          XT.dataSource.commitRecord(payload, options);
        }
      } else {
        // run this query against an instance database
        X.database.query(organization, query, callback);
      }
      return this;
    },

    didQuery: function (callback, err, res) {
      if (err) return callback(err);
      if (res !== undefined) {
        callback(null, res);
      } else {
        this.set("error", "no result from query");
      }
    },

    //.....................................
    // PROPERTY METHODS
    //

    model: function () {
      return XM.Session;
    }.property(),

    details: function () {
      var d = {
        id: this.get("id"),
        sid: this.get("sid"),
        lastModified: this.get("lastModified") || X.Session.timestamp(),
        created: this.get("created"),
        checksum: "",
        username: this.get("username"),
        organization: this.get("organization"),
        organizations: this.get("organizations"),
        socket: this.get("socket")
      };

      //X.debug("details", d);

      return d;
    }.property(),

    socket: function (socket) {
      if (arguments.length === 0) return this._socketId;
      this._socketId = socket.id;
      this.commit();
    }.property(),

    error: function () {
      if (arguments.length === 0) return this._error;
      var err = this._error = {}, args = X.$A(arguments);
      err.reason = args.shift();
      while (args.length > 0) {
        X.mixin(err, args.shift());
      }
    }.property(),

    //.....................................
    // PROXIED METHODS
    //

    load: function () {
      return X.Session.load.apply(this, arguments);
    }
  });

  X.mixin(X.Session, /** @lends X.Session */{

    cache: null,

    load: function (callback) {
      //X.log("load(): ");

      var K = this.get("model"),
        k = new K(),
        options = {};

      if (!callback) { callback = _.bind(this.didLoad, this); }
      options.id = this.get("sid") || -1;
      options.success = function (resp) { callback(null, resp); };
      options.error = function (model, err) { callback(err); };

      //X.log("load(): ", details);

      k.fetch(options);
      return this;
    },

    checksum: function () {
      //var hash = X.Session.sha1hash(), data = ;
      //data = X.typeOf(data) === X.T_HASH ? X.json(data) : data;
      //return hash.update(data).digest('hex');
    },

    init: function () {
      this.sid = X.Session.generateSID();
      this.created = X.Session.timestamp();
    },

    generateSID: function () {
      // http://www.ietf.org/rfc/rfc4122.txt
      var s = [];
      var hexDigits = "0123456789abcdef";
      for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
      }
      s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
      s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
      s[8] = s[13] = s[18] = s[23] = "-";

      var uuid = s.join("");
      return uuid;
    },

    timestamp: function () { return new Date().getTime(); },

    sha1hash: function () { return X.crypto.createHash('sha1'); },

    md5hash: function () { return X.crypto.createHash('md5'); },

    /**
      Polls database and logs users out upon session timeout. This method
      can be used to do any actions that are driven from the cache.
     */
    pollCache: function () {
      var sessions = new XM.SessionCollection(),
        i,
        diff,
        ok,
        now,
        model,
        socket,
        sockets,
        options = {};

      sockets = X.Server.getServer("Data").socketsFor("/clientsock");

      options.success = function () {
        ok = (X.options.datasource.sessionTimeout || 15) * 60000;
        now = X.Session.timestamp();

        //console.log("RUNNING", ok, now);

        if (sessions && sessions.length > 0) {
          for (i = 0; i < sessions.length; ++i) {
            model = sessions.at(i);
            diff = Math.abs(now - (model.get('lastModified') || 1000000000));
            if (diff > ok) {
              socket = sockets[model.get('socket')];
              if (socket) {
                socket.emit("timeout");
                socket.disconnect();
              }
              model.destroy();
            }
          }
        }
      };
      options.error = function (model, err) {
        // we have serious issues if we have an error here!
        return issue(X.fatal("error polling cache", err.message(), err.code));
      };
      sessions.fetch(options);
    },

    isActiveSession: function () {
    }
  });

}());
<|endoftext|>


<commit_before>window.onload = function() {
    var s = document.getElementsByTagName('div'), ti, i;
    for (i = 0; i < s.length; i++) s[i].setAttribute('tabindex', 0);
    if (!s) return;
    var big = { current: 0, forward: fwd, reverse: rev, go: go, length: s.length };
    window.big = big;
    function resize() {
        var w = window.innerWidth, h = window.innerHeight, e = s[big.current];
        e.style.fontSize = h + 'px';
        for (i = h - 2; i > 0 && (e.offsetWidth > w || e.offsetHeight > h); i -= 2) {
            e.style.fontSize = i + 'px';
        }
        e.style.marginTop = (h - e.offsetHeight) / 2 + 'px';
    }
    function go(n) {
        big.current = n;
        var e = s[n], t = parseInt(e.getAttribute('data-timeToNext') || 0, 10),
            notes = e.getElementsByTagName('notes');
        document.body.className = e.getAttribute('data-bodyclass') || '';
        for (i = 0; i < s.length; i++) s[i].style.display = 'none';
        e.style.display = 'inline';
        e.focus();
        for (i = 0; typeof console === 'object' && i < notes.length; i++) console.log('%c%s: %s', 'padding:5px;font-family:serif;font-size:18px;line-height:150%;', n, notes[i].innerHTML.trim());
        if (e.firstChild && e.firstChild.nodeName === 'IMG') {
            document.body.style.backgroundImage = 'url("' + e.firstChild.src + '")';
            e.firstChild.style.display = 'none';
            if ('classList' in e) e.classList.add('imageText');
        } else {
            document.body.style.backgroundImage = '';
            document.body.style.backgroundColor = e.style.backgroundColor;
        }
        if (ti !== undefined) window.clearInterval(ti);
        if (t > 0) ti = window.setTimeout(fwd, t * 1000);
        resize();
        if (window.location.hash !== n) window.location.hash = n;
        document.title = e.textContent || e.innerText;
    }
    document.onclick = function() { go(++big.current % s.length); };
    function fwd() { go(Math.min(s.length - 1, ++big.current)); }
    function rev() { go(Math.max(0, --big.current)); }
    document.onkeydown = function(e) {
        if (e.which === 39 || e.which === 34 || e.which === 40) fwd();
        if (e.which === 37 || e.which === 33 || e.which === 38) rev();
    };
    document.ontouchstart = function(e) {
        var x0 = e.changedTouches[0].pageX;
        document.ontouchend = function(e2) {
            var x1 = e2.changedTouches[0].pageX;
            if (x1 - x0 < 0) fwd();
            if (x1 - x0 > 0) rev();
        };
    };
    function parse_hash() {
        return Math.max(Math.min(s.length - 1,
            parseInt(window.location.hash.substring(1), 10)), 0);
    }
    if (window.location.hash) big.current = parse_hash() || big.current;
    window.onhashchange = function() {
        i = parse_hash();
        if (i !== big.current) go(i);
    };
    window.onresize = resize;
    go(big.current);
};
<commit_msg>Update big.js<commit_after>window.onload = function() {
    var s = document.getElementsByTagName('div'), ti, i;
    for (i = 0; i < s.length; i++) s[i].setAttribute('tabindex', 0);
    if (!s) return;
    var big = { current: 0, forward: fwd, reverse: rev, go: go, length: s.length };
    window.big = big;
    function resize() {
        var w = window.innerWidth, h = window.innerHeight, e = s[big.current];
        e.style.fontSize = h + 'px';
        for (i = h - 2; i > 0 && (e.offsetWidth > w || e.offsetHeight > h); i -= 2) {
            e.style.fontSize = i + 'px';
        }
        e.style.marginTop = (h - e.offsetHeight) / 2 + 'px';
    }
    function go(n) {
        big.current = n;
        var e = s[n], t = parseInt(e.getAttribute('data-timeToNext') || 0, 10),
            notes = e.getElementsByTagName('notes');
        document.body.className = e.getAttribute('data-bodyclass') || '';
        for (i = 0; i < s.length; i++) s[i].style.display = 'none';
        e.style.display = 'inline';
        e.focus();
        for (i = 0; typeof console === 'object' && i < notes.length; i++) console.log('%c%s: %s', 'padding:5px;font-family:serif;font-size:18px;line-height:150%;', n, notes[i].innerHTML.trim());
        if (e.firstChild && e.firstChild.nodeName === 'IMG') {
            document.body.style.background = 'url("' + e.firstChild.src + '") center center / ' + (e.dataset.stretch ? 'cover' : 'contain') + ' no-repeat fixed';
            e.firstChild.style.display = 'none';
            if ('classList' in e) e.classList.add('imageText');
        } else {
            document.body.style.background = '';
            document.body.style.backgroundColor = e.style.backgroundColor;
        }
        if (ti !== undefined) window.clearInterval(ti);
        if (t > 0) ti = window.setTimeout(fwd, t * 1000);
        resize();
        if (window.location.hash !== n) window.location.hash = n;
        document.title = e.textContent || e.innerText;
    }
    document.onclick = function() { go(++big.current % s.length); };
    function fwd() { go(Math.min(s.length - 1, ++big.current)); }
    function rev() { go(Math.max(0, --big.current)); }
    document.onkeydown = function(e) {
        if (e.which === 39 || e.which === 34 || e.which === 40) fwd();
        if (e.which === 37 || e.which === 33 || e.which === 38) rev();
    };
    document.ontouchstart = function(e) {
        var x0 = e.changedTouches[0].pageX;
        document.ontouchend = function(e2) {
            var x1 = e2.changedTouches[0].pageX;
            if (x1 - x0 < 0) fwd();
            if (x1 - x0 > 0) rev();
        };
    };
    function parse_hash() {
        return Math.max(Math.min(s.length - 1,
            parseInt(window.location.hash.substring(1), 10)), 0);
    }
    if (window.location.hash) big.current = parse_hash() || big.current;
    window.onhashchange = function() {
        i = parse_hash();
        if (i !== big.current) go(i);
    };
    window.onresize = resize;
    go(big.current);
};
<|endoftext|>


<commit_before>/**
 * Twig.js 0.8.2-4
 *
 * @copyright 2011-2015 John Roepke and the Twig.js Contributors
 * @license   Available under the BSD 2-Clause License
 * @link      https://github.com/justjohn/twig.js
 */

var Twig = (function (Twig) {

    Twig.VERSION = "0.8.2-4";

    return Twig;
})(Twig || {});
<commit_msg>Bump internal Twig.js version to "0.8.2-5"<commit_after>/**
 * Twig.js 0.8.2-4
 *
 * @copyright 2011-2015 John Roepke and the Twig.js Contributors
 * @license   Available under the BSD 2-Clause License
 * @link      https://github.com/justjohn/twig.js
 */

var Twig = (function (Twig) {

    Twig.VERSION = "0.8.2-5";

    return Twig;
})(Twig || {});
<|endoftext|>


<commit_before>// Sohm Al (normal)
// Nobody remembers what to do here, so here's triggers.
[{
  zoneRegex: /^Sohm Al$/
  triggers: [
    {
      id: 'Sohm Al Myath Stack',
      regex: /1B:........:(\y{Name}):....:....:0017:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Stack on YOU';
        return 'Stack on ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Spread',
      regex: /1B:........:(\y{Name}):....:....:00AE:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Move away from others';
        return 'Move away from ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Chyme',
      regex: /:Added new combatant Chyme Of The Mountain/
      alertText: function(data) { return 'Kill Chyme Add'; },
    },
    {
      id: 'Sohm Al Tioman Meteor',
      regex: /1B:........:(\y{Name}):....:....:0007:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'place meteor on edge';
      },
    },
  ]
}]
<commit_msg>Fix missing comma in sohm al triggers<commit_after>// Sohm Al (normal)
// Nobody remembers what to do here, so here's triggers.
[{
  zoneRegex: /^Sohm Al$/,
  triggers: [
    {
      id: 'Sohm Al Myath Stack',
      regex: /1B:........:(\y{Name}):....:....:0017:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Stack on YOU';
        return 'Stack on ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Spread',
      regex: /1B:........:(\y{Name}):....:....:00AE:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Move away from others';
        return 'Move away from ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Chyme',
      regex: /:Added new combatant Chyme Of The Mountain/
      alertText: function(data) { return 'Kill Chyme Add'; },
    },
    {
      id: 'Sohm Al Tioman Meteor',
      regex: /1B:........:(\y{Name}):....:....:0007:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'place meteor on edge';
      },
    },
  ]
}]
<|endoftext|>


<commit_before>/*
 * Creates a transform stream with a record in its closure that
 * pulls from streamed data desired properties (as listed in the record).
 */

'use strict';
var through = require('through2');
var winston = require('winston');
var resolveFields = require('./resolveFields');
var formatAddress = require('./formatAddress');

var objectMode = {objectMode: true};
var logger = winston;

function fieldFilter(record){
  var fields = record.fields;
  var name = record.name;
  //Chunk is a GeoJSON feature
  return through(objectMode, function(chunk, enc, cb){

    var props = chunk.properties;

    try{
      var vals = resolveFields(props, fields);
    }catch(e){
      logger.error(e);
      return cb(null);
    }

    var address = formatAddress(vals.Number, vals.Street, vals.City, vals.State, vals.Zip);

    if(address === null || chunk.geometry === null){
      logger.info('No valid street name or number for %s.\n\nOriginal fields: %s\n\nResolved fields: %s\n', name, JSON.stringify(props), JSON.stringify(vals));
      return cb(null);
    }

    var payload = {
      type: "Feature",
      properties: {
        address: address,
        number: vals.Number + '',
        street: vals.Street,
        city: vals.City,
        state: vals.State,
        zip: vals.Zip + ''
      },
      geometry: chunk.geometry
    }

    return cb(null, payload);
  });
}

fieldFilter.setLogger = function(newLogger){
  logger = newLogger;
}

module.exports = fieldFilter;
<commit_msg>Schema check in field filter<commit_after>/*
 * Creates a transform stream with a record in its closure that
 * pulls from streamed data desired properties (as listed in the record).
 */

'use strict';
var through = require('through2');
var winston = require('winston');
var resolveFields = require('./resolveFields');
var formatAddress = require('./formatAddress');

var objectMode = {objectMode: true};
var logger = winston;

function fieldFilter(record){
  var fields = record.fields;
  var name = record.name;
  //Chunk is a GeoJSON feature
  return through(objectMode, function(chunk, enc, cb){

    var props = chunk.properties;

    try{
      var vals = resolveFields(props, fields);
    }catch(e){
      logger.error(e);
      return cb(null);
    }

    var number = vals.Number;
    var street = vals.Street;
    var city = vals.City;
    var state = vals.State;
    var zip = vals.Zip;

    if(number === undefined || street === undefined || city === undefined || state === undefined || zip === undefined){
      return cb(new Error('The field mappings provided in the metadata file are not correct. Perhaps the schema has changed.'));
    }

    var address = formatAddress(number, street, city, state, zip);

    if(address === null || chunk.geometry === null){
      logger.info('No valid street name or number for %s.\n\nOriginal fields: %s\n\nResolved fields: %s\n', name, JSON.stringify(props), JSON.stringify(vals));
      return cb(null);
    }

    var payload = {
      type: "Feature",
      properties: {
        address: address,
        number: number + '',
        street: street,
        city: city,
        state: state,
        zip: zip + ''
      },
      geometry: chunk.geometry
    }

    return cb(null, payload);
  });
}

fieldFilter.setLogger = function(newLogger){
  logger = newLogger;
}

module.exports = fieldFilter;
<|endoftext|>