<commit_before>const _ = require('lodash');
const util = require('../../core/util.js');
const config = util.getConfig();
const dirs = util.dirs();
const moment = require('moment');

const log = require(dirs.core + 'log');
const Broker = require(dirs.gekko + '/exchange/gekkoBroker');

const Trader = function(next) {

  this.brokerConfig = {
    ...config.trader,
    ...config.watch,
    private: true
  }

  this.propogatedTrades = 0;

  this.broker = new Broker(this.brokerConfig);

  if(!this.broker.capabilities.gekkoBroker) {
    util.die('This exchange is not yet supported');
  }

  this.sync(() => {
    log.info('\t', 'Portfolio:');
    log.info('\t\t', this.portfolio.currency, this.brokerConfig.currency);
    log.info('\t\t', this.portfolio.asset, this.brokerConfig.asset);
    log.info('\t', 'Balance:');
    log.info('\t\t', this.balance, this.brokerConfig.currency);
    log.info('\t', 'Exposed:');
    log.info('\t\t',
      this.exposed ? 'yes' : 'no',
      `(${(this.exposure * 100).toFixed(2)}%)`
    );
    next();
  });

  this.sendInitialPortfolio = false;
  this.cancellingOrder = false;

  _.bindAll(this);
}

// teach our trader events
util.makeEventEmitter(Trader);

Trader.prototype.sync = function(next) {
  log.debug('syncing portfolio');
  this.broker.syncPrivateData(() => {
    this.price = this.broker.ticker.bid;
    this.setPortfolio();
    if(next) {
      next();
    }
  });
}

Trader.prototype.setPortfolio = function() {
  this.portfolio = {
    currency: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.currency
    ).amount,
    asset: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.asset
    ).amount
  }
  this.balance = this.portfolio.currency + this.portfolio.asset * this.price;
  this.exposure = (this.portfolio.asset * this.price) / this.balance;

  // if more than 10% of balance is in asset we are exposed
  this.exposed = this.exposure > 0.1;
}

Trader.prototype.processCandle = function(candle, done) {
  this.price = candle.close;
  this.setPortfolio();

  // on init
  if(!this.sendInitialPortfolio) {
    this.sendInitialPortfolio = true;
    this.deferredEmit('portfolioChange', {
      asset: this.portfolio.asset,
      currency: this.portfolio.currency
    });
    this.deferredEmit('portfolioValueChange', {
      balance: this.balance
    });
  } else if(this.exposed) {
    this.deferredEmit('portfolioValueChange', {
      balance: this.balance
    });
  }

  done();
}

Trader.prototype.processAdvice = function(advice) {
  const direction = advice.recommendation === 'long' ? 'buy' : 'sell';

  const id = 'trade-' + (++this.propogatedTrades);

  if(this.order) {
    if(this.order.side === direction) {
      return log.info('ignoring advice: already in the process to', direction);
    }

    if(this.cancellingOrder) {
      return log.info('ignoring advice: already cancelling previous', this.order.side, 'order');
    }

    log.info('Received advice to', direction, 'however Gekko is already in the process to', this.order.side);
    log.info('Canceling', this.order.side, 'order first');
    return this.cancelOrder(id, advice, () => this.processAdvice(advice));
  }

  let amount;

  if(direction === 'buy') {

    if(this.exposed) {
      log.info('NOT buying, already exposed');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.currency / this.price * 0.95;

    if(amount < this.broker.marketConfig.minimalOrder.amount) {
      log.info('NOT buying, not enough', this.brokerConfig.currency);
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Not enough to trade."
      });
    }

    log.info(
      'Trader',
      'Received advice to go long.',
      'Buying ', this.brokerConfig.asset
    );

  } else if(direction === 'sell') {

    if(!this.exposed) {
      log.info('NOT selling, already no exposure');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.asset * 0.95;

    if(amount < this.broker.marketConfig.minimalOrder.amount) {
      log.info('NOT selling, not enough', this.brokerConfig.currency);
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Not enough to trade."
      });
    }

    log.info(
      'Trader',
      'Received advice to go short.',
      'Selling ', this.brokerConfig.asset
    );
  }

  this.createOrder(direction, amount, advice, id);
}

Trader.prototype.createOrder = function(side, amount, advice, id) {
  const type = 'sticky';

  log.debug('Creating order to', side, amount, this.brokerConfig.asset);

  this.deferredEmit('tradeInitiated', {
    id,
    adviceId: advice.id,
    action: side,
    portfolio: this.portfolio,
    balance: this.balance
  });

  this.order = this.broker.createOrder(type, side, amount);

  this.order.on('filled', f => log.info('[ORDER] partial', side, ' fill, total filled:', f));
  this.order.on('statusChange', s => log.debug('[ORDER] statusChange:', s));

  this.order.on('error', e => {
    log.error('[ORDER] Gekko received error from GB:', e.message);
    log.debug(e);
    this.order = null;
    this.cancellingOrder = false;

    this.deferredEmit('tradeErrored', {
      id,
      adviceId: advice.id,
      date: moment(),
      reason: e.message
    });

  });
  this.order.on('completed', () => {
    this.order.createSummary((err, summary) => {
      log.info('[ORDER] summary:', summary);
      this.order = null;
      this.sync(() => {

        let cost;
        if(_.isNumber(summary.feePercent)) {
          cost = summary.feePercent / 100 * summary.amount * summary.price;
        }

        let effectivePrice;
        if(_.isNumber(summary.feePercent)) {
          if(side === 'buy') {
            effectivePrice = summary.price * (1 + summary.feePercent / 100);
          } else {
            effectivePrice = summary.price * (1 - summary.feePercent / 100);
          }
        } else {
          log.debug('WARNING: exchange did not provide fee information, assuming no fees..');
          effectivePrice = summary.price;
        }

        this.deferredEmit('tradeCompleted', {
          id,
          adviceId: advice.id,
          action: summary.side,
          cost,
          amount: summary.amount,
          price: summary.price,
          portfolio: this.portfolio,
          balance: this.balance,
          date: summary.date,
          feePercent: summary.feePercent,
          effectivePrice
        });
      });
    })
  });
}

Trader.prototype.cancelOrder = function(id, advice, next) {

  if(!this.order) {
    return next();
  }

  this.cancellingOrder = true;

  this.order.removeAllListeners();
  this.order.cancel();
  this.order.once('completed', () => {
    this.order = null;
    this.deferredEmit('tradeCancelled', {
      id,
      adviceId: advice.id,
      date: moment()
    });
    this.sync(next);
  });
}

module.exports = Trader;
<commit_msg>set cancelling to false after canceling, see #2323<commit_after>const _ = require('lodash');
const util = require('../../core/util.js');
const config = util.getConfig();
const dirs = util.dirs();
const moment = require('moment');

const log = require(dirs.core + 'log');
const Broker = require(dirs.gekko + '/exchange/gekkoBroker');

const Trader = function(next) {

  this.brokerConfig = {
    ...config.trader,
    ...config.watch,
    private: true
  }

  this.propogatedTrades = 0;

  this.broker = new Broker(this.brokerConfig);

  if(!this.broker.capabilities.gekkoBroker) {
    util.die('This exchange is not yet supported');
  }

  this.sync(() => {
    log.info('\t', 'Portfolio:');
    log.info('\t\t', this.portfolio.currency, this.brokerConfig.currency);
    log.info('\t\t', this.portfolio.asset, this.brokerConfig.asset);
    log.info('\t', 'Balance:');
    log.info('\t\t', this.balance, this.brokerConfig.currency);
    log.info('\t', 'Exposed:');
    log.info('\t\t',
      this.exposed ? 'yes' : 'no',
      `(${(this.exposure * 100).toFixed(2)}%)`
    );
    next();
  });

  this.sendInitialPortfolio = false;
  this.cancellingOrder = false;

  _.bindAll(this);
}

// teach our trader events
util.makeEventEmitter(Trader);

Trader.prototype.sync = function(next) {
  log.debug('syncing portfolio');
  this.broker.syncPrivateData(() => {
    this.price = this.broker.ticker.bid;
    this.setPortfolio();
    if(next) {
      next();
    }
  });
}

Trader.prototype.setPortfolio = function() {
  this.portfolio = {
    currency: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.currency
    ).amount,
    asset: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.asset
    ).amount
  }
  this.balance = this.portfolio.currency + this.portfolio.asset * this.price;
  this.exposure = (this.portfolio.asset * this.price) / this.balance;

  // if more than 10% of balance is in asset we are exposed
  this.exposed = this.exposure > 0.1;
}

Trader.prototype.processCandle = function(candle, done) {
  this.price = candle.close;
  this.setPortfolio();

  // on init
  if(!this.sendInitialPortfolio) {
    this.sendInitialPortfolio = true;
    this.deferredEmit('portfolioChange', {
      asset: this.portfolio.asset,
      currency: this.portfolio.currency
    });
    this.deferredEmit('portfolioValueChange', {
      balance: this.balance
    });
  } else if(this.exposed) {
    this.deferredEmit('portfolioValueChange', {
      balance: this.balance
    });
  }

  done();
}

Trader.prototype.processAdvice = function(advice) {
  const direction = advice.recommendation === 'long' ? 'buy' : 'sell';

  const id = 'trade-' + (++this.propogatedTrades);

  if(this.order) {
    if(this.order.side === direction) {
      return log.info('ignoring advice: already in the process to', direction);
    }

    if(this.cancellingOrder) {
      return log.info('ignoring advice: already cancelling previous', this.order.side, 'order');
    }

    log.info('Received advice to', direction, 'however Gekko is already in the process to', this.order.side);
    log.info('Canceling', this.order.side, 'order first');
    return this.cancelOrder(id, advice, () => this.processAdvice(advice));
  }

  let amount;

  if(direction === 'buy') {

    if(this.exposed) {
      log.info('NOT buying, already exposed');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.currency / this.price * 0.95;

    if(amount < this.broker.marketConfig.minimalOrder.amount) {
      log.info('NOT buying, not enough', this.brokerConfig.currency);
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Not enough to trade."
      });
    }

    log.info(
      'Trader',
      'Received advice to go long.',
      'Buying ', this.brokerConfig.asset
    );

  } else if(direction === 'sell') {

    if(!this.exposed) {
      log.info('NOT selling, already no exposure');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.asset * 0.95;

    if(amount < this.broker.marketConfig.minimalOrder.amount) {
      log.info('NOT selling, not enough', this.brokerConfig.currency);
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Not enough to trade."
      });
    }

    log.info(
      'Trader',
      'Received advice to go short.',
      'Selling ', this.brokerConfig.asset
    );
  }

  this.createOrder(direction, amount, advice, id);
}

Trader.prototype.createOrder = function(side, amount, advice, id) {
  const type = 'sticky';

  log.debug('Creating order to', side, amount, this.brokerConfig.asset);

  this.deferredEmit('tradeInitiated', {
    id,
    adviceId: advice.id,
    action: side,
    portfolio: this.portfolio,
    balance: this.balance
  });

  this.order = this.broker.createOrder(type, side, amount);

  this.order.on('filled', f => log.info('[ORDER] partial', side, ' fill, total filled:', f));
  this.order.on('statusChange', s => log.debug('[ORDER] statusChange:', s));

  this.order.on('error', e => {
    log.error('[ORDER] Gekko received error from GB:', e.message);
    log.debug(e);
    this.order = null;
    this.cancellingOrder = false;

    this.deferredEmit('tradeErrored', {
      id,
      adviceId: advice.id,
      date: moment(),
      reason: e.message
    });

  });
  this.order.on('completed', () => {
    this.order.createSummary((err, summary) => {
      log.info('[ORDER] summary:', summary);
      this.order = null;
      this.sync(() => {

        let cost;
        if(_.isNumber(summary.feePercent)) {
          cost = summary.feePercent / 100 * summary.amount * summary.price;
        }

        let effectivePrice;
        if(_.isNumber(summary.feePercent)) {
          if(side === 'buy') {
            effectivePrice = summary.price * (1 + summary.feePercent / 100);
          } else {
            effectivePrice = summary.price * (1 - summary.feePercent / 100);
          }
        } else {
          log.debug('WARNING: exchange did not provide fee information, assuming no fees..');
          effectivePrice = summary.price;
        }

        this.deferredEmit('tradeCompleted', {
          id,
          adviceId: advice.id,
          action: summary.side,
          cost,
          amount: summary.amount,
          price: summary.price,
          portfolio: this.portfolio,
          balance: this.balance,
          date: summary.date,
          feePercent: summary.feePercent,
          effectivePrice
        });
      });
    })
  });
}

Trader.prototype.cancelOrder = function(id, advice, next) {

  if(!this.order) {
    return next();
  }

  this.cancellingOrder = true;

  this.order.removeAllListeners();
  this.order.cancel();
  this.order.once('completed', () => {
    this.order = null;
    this.cancellingOrder = false;
    this.deferredEmit('tradeCancelled', {
      id,
      adviceId: advice.id,
      date: moment()
    });
    this.sync(next);
  });
}

module.exports = Trader;
<|endoftext|>


<commit_before>/* globals describe, it */
const expect = require('chai').expect
const sum = require('../src/sum')

describe('sum', () => {
  it('should sum two numbers', () => {
    const result = sum(1, 2)

    expect(result).to.be.deep.equal(3)
  })
})
<commit_msg>falha no teste<commit_after>/* globals describe, it */
const expect = require('chai').expect
const sum = require('../src/sum')

describe('sum', () => {
  it('should sum two numbers', () => {
    const result = sum(1, 2)

    expect(result).to.be.deep.equal(5)
  })
})
<|endoftext|>


<commit_before>Divvy.Settings = {};

Divvy.Settings.init = function()
{
	/*
	 * Window elements
	 */
	this.win = Ti.UI.createWindow({
		title: 'Settings',
		barColor: Divvy.winBarColor,
		barImage: Divvy.winBarImage,
		modal:true,
		orientationModes: [
			Titanium.UI.PORTRAIT
		],
		backgroundColor: '#d6d8de'
	});

	//Create the Button
	this.doneButtonBar = Ti.UI.createButtonBar({
		labels: ['Done'],
		systemButton: Ti.UI.iPhone.SystemButtonStyle.DONE,
		backgroundColor: '5383e4'
	});

	this.doneButtonBar.addEventListener('click', function(e)
	{
		Divvy.Settings.done();
	});

	this.win.rightNavButton = this.doneButtonBar;

	//Start the TableView
	this.tableview = Ti.UI.createTableView({
		style: Ti.UI.iPhone.TableViewStyle.GROUPED,
	});

	//Table view will have sections
	this.general = Ti.UI.createTableViewSection();
	this.general.headerTitle = "General";

	//Create a row in first section
	this.row_name = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
	});
			//Customize the row
	this.label_name = Ti.UI.createLabel({
		text: 'Name',
		left: 10,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.textarea_name = Ti.UI.createTextField({
		hintText: 'Optional',
		left: 100,
		width: (Ti.Platform.osname == "ipad") ? '570' : '190', height: 24,
		editable: true,
		color: '#385487',
		keyboardType: Ti.UI.KEYBOARD,
		clearButtonMode: Titanium.UI.INPUT_BUTTONMODE_ONFOCUS,
	});

	//Add the row
	this.row_name.add(this.textarea_name);
	this.row_name.add(this.label_name);

	//Create row in first section
	this.row_push = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
	});

	//Customize the row
	this.label_push = Ti.UI.createLabel({
		text: 'Push Notifications',
		left: 10,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.switch_push = Ti.UI.createSwitch({
		value: false,
		left: 210
	});

	//Make the object clickable
	this.switch_push.addEventListener('switch', function(e){
		Ti.API.info('Basic Switch value = ' + e.value + ' act val ' + basicSwitch.value);
	});

	//Add the row
	this.row_push.add(this.switch_push);
	this.row_push.add(this.label_push);

	//Create row in first section
	this.row_save = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
	});

	//Customize row
	this.label_save = Ti.UI.createLabel({
		text: 'Local Device Save',
		left: 10,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.switch_save = Ti.UI.createSwitch({
		value: false,
		left: 210
	});

	//Make object clickable
	this.switch_save.addEventListener('switch', function(e){
		Ti.API.info('Basic Switch value = ' + e.value + ' act val ' + basicSwitch.value);
	});

	//Add the row
	this.row_save.add(this.switch_save);
	this.row_save.add(this.label_save);

	//this.general.add(this.row_name);
	this.general.add(this.row_push);
	this.general.add(this.row_save);
	this.tableview.add(this.general);

	//Create another section in table view
	this.pro = Ti.UI.createTableViewSection();
	this.pro.headerTitle = "Upgrade";

	//Create a row in second section
	this.row_pro = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
		height: 80
	});

	this.image_pro = Ti.UI.createImageView({
		image: 'images/appicon.png',
		hires: true,
		height: 57,
		width: 57,
		top: 10,
		left: 10
	});

	this.row_pro.add(this.image_pro);

	//Customize that row
	this.label_pro = Ti.UI.createLabel({
		text: 'DivvyCam Pro',
		height: 18,
		top: 20,
		left: 80,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.label_price = Ti.UI.createLabel({
		text: '$0.00',
		height: 17,
		top: 38,
		left: 80,
		font:{fontSize: 14}
	});

	this.button_buy = Ti.UI.createButton({
		title: 'Purchase',
		top: 25,
		left: 210,
		height: 30,
		width: 75,
	});

	this.row_pro.add(this.button_buy);
	this.row_pro.add(this.label_price);
	this.row_pro.add(this.label_pro);

	//Create row for second section
	this.row_info = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
		height: 80
	});

	//Customize that row
	this.label_info = Ti.UI.createLabel({
		text: 'Description',
		height: 'auto',
		top: 10,
		left: 10,
		font: { fontSize: 14, fontWeight: 'bold' }
	});

	this.label_description = Ti.UI.createLabel({
		text: 'Enables you to have unlimited buckets, better photo quality and no advertisements.',
		height: 'auto',
		top: 10,
		left: 100,
		font: { fontSize: 14 }
	});

	this.row_info.add(this.label_info);
	this.row_info.add(this.label_description);

	//Add to the second section
	this.pro.add(this.row_pro);
	this.pro.add(this.row_info);
	this.tableview.add(this.pro);

	//Add the sections to the tableview
	tableData = [this.general,this.pro];
	this.tableview.setData(tableData);
	this.win.add(this.tableview);

}
Divvy.Settings.open = function()
{
	this.win.open();
};

Divvy.Settings.done = function()
{
	this.win.close();
};<commit_msg>8===D<commit_after>Divvy.Settings = {};

Divvy.Settings.init = function()
{
	/*
	 * Window elements
	 */
	this.win = Ti.UI.createWindow({
		title: 'Settings',
		barColor: Divvy.winBarColor,
		barImage: Divvy.winBarImage,
		modal:true,
		orientationModes: [
			Titanium.UI.PORTRAIT
		],
		backgroundColor: '#d6d8de'
	});
	//Check events done in the window.
	this.win.addEventListener('close', function(){
		Divvy.Settings.save();
	});

	//Create the Button
	this.doneButtonBar = Ti.UI.createButtonBar({
		labels: ['Done'],
		systemButton: Ti.UI.iPhone.SystemButtonStyle.DONE,
		backgroundColor: '5383e4'
	});

	this.doneButtonBar.addEventListener('click', function(e)
	{
		Divvy.Settings.done();
	});

	this.win.rightNavButton = this.doneButtonBar;

	//Start the TableView
	this.tableview = Ti.UI.createTableView({
		style: Ti.UI.iPhone.TableViewStyle.GROUPED,
	});

	//Table view will have sections
	this.general = Ti.UI.createTableViewSection();
	this.general.headerTitle = "General";

	//Create a row in first section
	this.row_name = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
	});
			//Customize the row
	this.label_name = Ti.UI.createLabel({
		text: 'Name',
		left: 10,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.textarea_name = Ti.UI.createTextField({
		hintText: 'Optional',
		left: 100,
		width: (Ti.Platform.osname == "ipad") ? '570' : '190', height: 24,
		editable: true,
		color: '#385487',
		keyboardType: Ti.UI.KEYBOARD,
		clearButtonMode: Titanium.UI.INPUT_BUTTONMODE_ONFOCUS,
	});

	//Add the row
	this.row_name.add(this.textarea_name);
	this.row_name.add(this.label_name);

	//Create row in first section
	this.row_push = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
	});

	//Customize the row
	this.label_push = Ti.UI.createLabel({
		text: 'Push Notifications',
		left: 10,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.switch_push = Ti.UI.createSwitch({
		value: false,
		left: 210
	});

	//Add the row
	this.row_push.add(this.switch_push);
	this.row_push.add(this.label_push);

	//Create row in first section
	this.row_save = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
	});

	//Customize row
	this.label_save = Ti.UI.createLabel({
		text: 'Local Device Save',
		left: 10,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.switch_save = Ti.UI.createSwitch({
		value: false,
		left: 210
	});

	//Add the row
	this.row_save.add(this.switch_save);
	this.row_save.add(this.label_save);

	//this.general.add(this.row_name);
	this.general.add(this.row_push);
	this.general.add(this.row_save);
	this.tableview.add(this.general);

	//Create another section in table view
	this.pro = Ti.UI.createTableViewSection();
	this.pro.headerTitle = "Upgrade";

	//Create a row in second section
	this.row_pro = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
		height: 80
	});

	this.image_pro = Ti.UI.createImageView({
		image: 'images/appicon.png',
		hires: true,
		height: 57,
		width: 57,
		top: 10,
		left: 10
	});

	this.row_pro.add(this.image_pro);

	//Customize that row
	this.label_pro = Ti.UI.createLabel({
		text: 'DivvyCam Pro',
		height: 18,
		top: 20,
		left: 80,
		font: { fontSize: 16, fontWeight: 'bold' }
	});

	this.label_price = Ti.UI.createLabel({
		text: '$0.00',
		height: 17,
		top: 38,
		left: 80,
		font:{fontSize: 14}
	});

	this.button_buy = Ti.UI.createButton({
		title: 'Purchase',
		top: 25,
		left: 210,
		height: 30,
		width: 75,
	});

	this.row_pro.add(this.button_buy);
	this.row_pro.add(this.label_price);
	this.row_pro.add(this.label_pro);

	//Create row for second section
	this.row_info = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
		height: 80
	});

	//Customize that row
	this.label_info = Ti.UI.createLabel({
		text: 'Description',
		height: 'auto',
		top: 10,
		left: 10,
		font: { fontSize: 14, fontWeight: 'bold' }
	});

	this.label_description = Ti.UI.createLabel({
		text: 'Enables you to have unlimited buckets, better photo quality and no advertisements.',
		height: 'auto',
		top: 10,
		left: 100,
		font: { fontSize: 14 }
	});

	//Checking for previous purchase row
	this.row_purchase = Ti.UI.createTableViewRow({
		selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
		hasChild: true
	});

	this.label_purchase = Ti.UI.createLabel({
		text: 'Restore Previous Purchase',
		height: 'auto',
		top: 10,
		left: 10,
		font: { fontSize: 14, fontWeight: 'bold' }
	});

	this.row_purchase.addEventListener('click', function(e)
	{

	});

	this.row_info.add(this.label_info);
	this.row_info.add(this.label_description);
	this.row_purchase.add(this.label_purchase);

	//Add to the second section
	this.pro.add(this.row_pro);
	this.pro.add(this.row_info);
	this.pro.add(this.row_purchase);
	this.tableview.add(this.pro);

	//Add the sections to the tableview
	tableData = [this.general,this.pro];
	this.tableview.setData(tableData);
	this.win.add(this.tableview);

};

Divvy.Settings.open = function()
{
	this.switch_push.value = Ti.App.Properties.getBool("push_notifications");
	this.switch_save.value = Ti.App.Properties.getBool("save_device");
	this.win.open();
};

Divvy.Settings.done = function()
{
	this.win.close();
};
Divvy.Settings.save = function()
{
	var push_notifications = Divvy.Settings.switch_push.value;
	Ti.App.Properties.setBool("push_notifications", push_notifications);

	var save_device = Divvy.Settings.switch_save.value;
	Ti.App.Properties.setBool("save_device", save_device);

};

<|endoftext|>


<commit_before>/* eslint-env jest */
const { execSync } = require('child_process')

describe('runjs', () => {
  let cwd

  function sh (cmd) {
    return execSync(cmd, { cwd, stdio: 'inherit' })
  }

  describe('with babel transpiler', () => {
    beforeAll(() => {
      cwd = './test/e2e/sandbox/babel'
      sh('yarn')
      sh('rm -f ./node_modules/runjs')
      sh('ln -s ../../../../../ ./node_modules/runjs')
    })

    it('executes simple task', () => {
      sh('../../../../bin/run.js echo 1 2 3 --foo --bar')
    })

    it('executes shell commands in a task', () => {
      sh('../../../../bin/run.js commands')
    })

    it('executes name spaced tasks', () => {
      sh('../../../../bin/run.js nested:echo 1 2 3 --foo --bar')
    })

    it('includes ./node_modules/.bin to PATH when executing commands', () => {
      sh('cp -p ./scripts/hello.js ./node_modules/.bin/hello')
      sh('../../../../bin/run.js localbin')
    })

    it('executes tasks with async and await', () => {
      sh('../../../../bin/run.js asyncawait')
    })
  })

  describe('with typescript transpiler', () => {
    beforeAll(() => {
      cwd = './test/e2e/sandbox/typescript'
      sh('yarn')
      sh('rm -f ./node_modules/runjs')
      sh('ln -s ../../../../../ ./node_modules/runjs')
    })

    it('executes simple task', () => {
      sh('../../../../bin/run.js echo 1 2 3 --foo --bar')
    })

    it('executes shell commands in a task', () => {
      sh('../../../../bin/run.js commands')
    })

    it('executes name spaced tasks', () => {
      sh('../../../../bin/run.js nested:echo 1 2 3 --foo --bar')
    })

    it('includes ./node_modules/.bin to PATH when executing commands', () => {
      sh('cp -p ../babel/scripts/hello.js ./node_modules/.bin/hello')
      sh('../../../../bin/run.js localbin')
    })

    it('executes tasks with async and await', () => {
      sh('../../../../bin/run.js asyncawait')
    })
  })
})
<commit_msg>E2E tests: test output from tasks<commit_after>/* eslint-env jest */
const { execSync } = require('child_process')

describe('runjs', () => {
  let cwd

  function sh (cmd) {
    return execSync(cmd, { cwd, stdio: 'pipe' }).toString()
  }

  describe('with babel transpiler', () => {
    beforeAll(() => {
      cwd = './test/e2e/sandbox/babel'
      sh('yarn')
      sh('rm -f ./node_modules/runjs')
      sh('ln -s ../../../../../ ./node_modules/runjs')
    })

    it('executes simple task', () => {
      expect(sh('../../../../bin/run.js echo 1 2 3 --foo --bar'))
        .toContain("echo [ '1', '2', '3' ] { foo: true, bar: true }")
    })

    it('executes shell commands in a task', () => {
      expect(sh('../../../../bin/run.js commands'))
        .toContain(
          'echo "sync terminal"\nsync terminal\necho "sync pipe"\noutput sync pipe\n\n' +
          'echo "async terminal"\necho "async terminal"\nasync terminal\noutput async terminal')
    })

    it('executes name spaced tasks', () => {
      expect(sh('../../../../bin/run.js nested:echo 1 2 3 --foo --bar'))
        .toContain("echo [ '1', '2', '3' ] { foo: true, bar: true }")
    })

    it('includes ./node_modules/.bin to PATH when executing commands', () => {
      sh('cp -p ./scripts/hello.js ./node_modules/.bin/hello')
      expect(sh('../../../../bin/run.js localbin')).toContain('Hello!')
    })

    it('executes tasks with async and await', () => {
      expect(sh('../../../../bin/run.js asyncawait'))
        .toContain('echo "async and await"\noutput async and await\n\nafter await')
    })
  })

  describe('with typescript transpiler', () => {
    beforeAll(() => {
      cwd = './test/e2e/sandbox/typescript'
      sh('yarn')
      sh('rm -f ./node_modules/runjs')
      sh('ln -s ../../../../../ ./node_modules/runjs')
    })

    it('executes simple task', () => {
      expect(sh('../../../../bin/run.js echo 1 2 3 --foo --bar'))
        .toContain("echo [ '1', '2', '3' ] { foo: true, bar: true }")
    })

    it('executes shell commands in a task', () => {
      expect(sh('../../../../bin/run.js commands'))
        .toContain(
          'echo "sync terminal"\nsync terminal\necho "sync pipe"\noutput sync pipe\n\n' +
          'echo "async terminal"\necho "async terminal"\nasync terminal\noutput async terminal')
    })

    it('executes name spaced tasks', () => {
      expect(sh('../../../../bin/run.js nested:echo 1 2 3 --foo --bar'))
        .toContain("echo [ '1', '2', '3' ] { foo: true, bar: true }")
    })

    it('includes ./node_modules/.bin to PATH when executing commands', () => {
      sh('cp -p ../babel/scripts/hello.js ./node_modules/.bin/hello')
      expect(sh('../../../../bin/run.js localbin')).toContain('Hello!')
    })

    it('executes tasks with async and await', () => {
      expect(sh('../../../../bin/run.js asyncawait'))
        .toContain('echo "async and await"\noutput async and await\n\nafter await')
    })
  })
})
<|endoftext|>


<commit_before>var express = require('express');
var router = express.router();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');

router.use(bodyParser.urlencoded( {extended: true} ));
router.use(methodOverride(function(req, res) {
	if (req.body && typeof req.body === 'object' && '_method' in req.body) {
		// look in urlencoded POST bodies and delete it
		var method = req.body._method;
		delete req.body._method;
		return method;
	}
}))

// build GET route for grabbing 'Dishwashers' from the database to display
// build POST route for creating new 'Dishwashers'

router.route('/')
	.get(function(req, res, next) {
		// retrieve all 'dishwashers' from mongo
		mongoose.model('Dishwasher').find({}, function(err, dishwashers) {
			if (err) {
				return console.error(err);
			}
			else {
				// respond to both HTML and JSON
				// JSON response requires 'Accept: application/json;' in Request Header
				res.format({
					// HTML response will render the index.jade file in the views/blobs folder.
					// We are also setting "dishwashers" to be an accessible variable in our jade view
					html: function() {
						res.render('dishwashers/index', { title: 'All dishwashers', "dishwashers" : dishwashers });
					},
					// JSON responses will show all dishwashers in JSON format
					json: function() {
						res.json(dishwashers);
					}
				});
			}
		});
	})
	.post(function(req, res) {
		// Gets the values from a post request. This can be done through forms or REST calls. They rely on the "name" attribute for forms
		var b_name = req.body.brand_name;
		var m_name = req.body.model_name;
		var m_number = req.body.model_number;
		var m_type = req.body.type;
		var m_gpc = req.body.gallons_per_cycle;
		var m_ef = req.body.energy_factor;
		var m_kpy = req.body.kwh_per_year;

		// this is where we tell mongoose to create the model and assign the above variables to the respective db fields
		mongoose.model('Dishwasher').create( {
			brand_name: b_name;
			model_name: m_name;
			model_number: m_number;
			type: m_type;
			gallons_per_cycle: m_gpc;
			energy_factor: m_ef;
			kwh_per_year: m_kpy;
		}, function(err, dishwasher) {
			// if response not valid, handle error
			if (err) {
				res.send("Couldn't add record to the database");
			}
			// if response is valid, create a dishwasher entry
			else {
				console.log('POST successful. dishwasher entry created: ' + dishwasher);
				res.format( {
					// make HTML response set location and redirect back to home page
					html: function() {
						// if success, set header so address bar doesn't still say /add
						res.location('dishwashers');
						res.redirect('/dishwashers');
					},
					// handle json
					json: function() {
						res.json(dishwasher);
					}
				});
			}
		})
	});

// GET a new Dishwasher page
router.get('/new', function(req, res) {
	res.render('dishwashers/new', { title: 'Add new dishwasher' });
});

<commit_msg>build route to edit/update document through standard web form<commit_after>var express = require('express');
var router = express.router();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');

router.use(bodyParser.urlencoded( {extended: true} ));
router.use(methodOverride(function(req, res) {
	if (req.body && typeof req.body === 'object' && '_method' in req.body) {
		// look in urlencoded POST bodies and delete it
		var method = req.body._method;
		delete req.body._method;
		return method;
	}
}))

// build GET route for grabbing 'Dishwashers' from the database to display
// build POST route for creating new 'Dishwashers'

router.route('/')
	.get(function(req, res, next) {
		// retrieve all 'dishwashers' from mongo
		mongoose.model('Dishwasher').find({}, function(err, dishwashers) {
			if (err) {
				return console.error(err);
			}
			else {
				// respond to both HTML and JSON
				// JSON response requires 'Accept: application/json;' in Request Header
				res.format({
					// HTML response will render the index.jade file in the views/blobs folder.
					// We are also setting "dishwashers" to be an accessible variable in our jade view
					html: function() {
						res.render('dishwashers/index', { title: 'All dishwashers', "dishwashers" : dishwashers });
					},
					// JSON responses will show all dishwashers in JSON format
					json: function() {
						res.json(dishwashers);
					}
				});
			}
		});
	})
	.post(function(req, res) {
		// Gets the values from a post request. This can be done through forms or REST calls. They rely on the "name" attribute for forms
		var b_name = req.body.brand_name;
		var m_name = req.body.model_name;
		var m_number = req.body.model_number;
		var m_type = req.body.type;
		var m_gpc = req.body.gallons_per_cycle;
		var m_ef = req.body.energy_factor;
		var m_kpy = req.body.kwh_per_year;

		// this is where we tell mongoose to create the model and assign the above variables to the respective db fields
		mongoose.model('Dishwasher').create( {
			brand_name: b_name;
			model_name: m_name;
			model_number: m_number;
			type: m_type;
			gallons_per_cycle: m_gpc;
			energy_factor: m_ef;
			kwh_per_year: m_kpy;
		}, function(err, dishwasher) {
			// if response not valid, handle error
			if (err) {
				res.send("Couldn't add record to the database");
			}
			// if response is valid, create a dishwasher entry
			else {
				console.log('POST successful. dishwasher entry created: ' + dishwasher);
				res.format( {
					// make HTML response set location and redirect back to home page
					html: function() {
						// if success, set header so address bar doesn't still say /add
						res.location('dishwashers');
						res.redirect('/dishwashers');
					},
					// handle json
					json: function() {
						res.json(dishwasher);
					}
				});
			}
		})
	});

// GET a new Dishwasher page
router.get('/new', function(req, res) {
	res.render('dishwashers/new', { title: 'Add new dishwasher' });
});

// Route middleware to validate :id
router.param('id', function(req, res, next, id) {
	// console.log('making sure that ' + id + ' exists');
	// this finds the ID in the mongo database
	mongoose.model('Dishwasher').findById(id, function(err, dishwasher) {
		// if no dishwasher found for this id, respond with the ol'404
		if (err) {
			console.log(id + ' not found');
			// set the response status to 404
			res.status(404);
			var err = new Error('Not Found');
			err.status = 404;
			res.format({
				// handle html
				html: function() {
					next(err);
				},
				// handle json
				json: function() {
					res.json({message : err.status + ' ' + err});
				}
			});
		// if a dishwasher IS found for this id, continue
		}
		else {
			// uncomment this next line if you want to see every JSON document response for every GET/PUT/DELETE call
            // console.log(blob);
            // once validation is complete, save the new item in the request
			req.id = id;
			// on to the next one
			next();
		}
	});
});

// GET an individual dishwasher item by ID in order to display it
router.route('/:id').get(function(req, res) {
	mongoose.model('Dishwasher').findById(req.id, function(err, dishwasher) {
		if (err) {
			console.log("GET error while retrieving " + err);
		}
		else {
			console.log("GET retrieving ID: " + dishwasher._id);
			res.format({
				// handle HTML
				html: function() {
					res.render('dishwashers/show', {"dishwasher" : dishwasher });
				},
				// handle JSON
				json: function() {
					res.json(dishwasher);
				}
			});
		}
	});
});

// route to edit/update document through standard web form
router.route('/:id/edit', function(req, res) {
	// search for the dishwasher entry within mongodb
	mongoose.model('Dishwasher').findById(req.id, function(err, dishwasher) {
		if (err) {
			// handle error
			console.log('GET error while retrieving ' + err);
		}
		else {
			// return the dishwasher
			console.log('GET retrieved ID ' + dishwasher._id);
			res.format({
				// handle HTML response to render 'edit.jade'
				html: function() {
					res.render('dishwashers/edit', { title: 'Dishwasher' + dishwasher._id, "dishwasher" : dishwasher });
				},
				// handle JSON response by returning JSON output
				json: function() {
					res.json(dishwasher);
				}
			});
		}
	});
});

<|endoftext|>


<commit_before>'use strict';

const Router = require('express').Router;
const interactionHandler = require('../controller/interactionhandler');
const rxnormidHandler = require('../controller/rxnormidhandler');
const HandleError = require('../controller/errhandler');
const DrugSchema = require('../models/drugschema');
const jsonParser = require('body-parser').json();
let drugRouter = Router();

drugRouter.post('/newDrug', jsonParser, function(req, res, next) {
  let errz = HandleError(400, next, 'invalid id');
  if(!req.body.drug){
    return errz();
  }
  rxnormidHandler(req.body.drug)
  .then((rxnormId) => {
    return interactionHandler(rxnormId);
  }).then((interactions) => {
    let newDrug = new DrugSchema({'drug': req.body.drug, 'interactions': interactions});
    newDrug.save((err, drugData) => {
      if (err) return next(err);
      res.send(drugData);
    });
  });
});

drugRouter.get('/:drugId', function(req, res, next) {
  let DBError = HandleError(400, next, 'invalid id');
  let Err404 = HandleError(404, next);
  DrugSchema.findOne({'_id': req.params.drugId}).then((data) => {
    if (!data) return next(Err404(new Error('drug not found.')));
    res.json(data);
  }, DBError);
});

drugRouter.get('/allDrugs', function(req, res, next) {
  DrugSchema.find().then(res.json.bind(res), HandleError(500, next, 'Server Error'));
});

module.exports = exports = drugRouter;
<commit_msg>fixed alldrugs bug<commit_after>'use strict';

const Router = require('express').Router;
const interactionHandler = require('../controller/interactionhandler');
const rxnormidHandler = require('../controller/rxnormidhandler');
const HandleError = require('../controller/errhandler');
const DrugSchema = require('../models/drugschema');
const jsonParser = require('body-parser').json();
let drugRouter = Router();

drugRouter.post('/newDrug', jsonParser, function(req, res, next) {
  let errz = HandleError(400, next, 'invalid id');
  if(!req.body.drug){
    return errz();
  }
  rxnormidHandler(req.body.drug)
  .then((rxnormId) => {
    return interactionHandler(rxnormId);
  }).then((interactions) => {
    let newDrug = new DrugSchema({'drug': req.body.drug, 'interactions': interactions});
    newDrug.save((err, drugData) => {
      if (err) return next(err);
      res.send(drugData);
    });
  });
});

drugRouter.get('/allDrugs', function(req, res, next) {
  DrugSchema.find().then(res.json.bind(res), HandleError(500, next, 'Server Error'));
});

drugRouter.get('/:drugId', function(req, res, next) {
  let DBError = HandleError(400, next, 'invalid id');
  let Err404 = HandleError(404, next);
  DrugSchema.findOne({'_id': req.params.drugId}).then((data) => {
    if (!data) return next(Err404(new Error('drug not found.')));
    res.json(data);
  }, DBError);
});

module.exports = exports = drugRouter;
<|endoftext|>


<commit_before>
'use strict';

var should= require('should')
var assert= require('assert')
var path= require('path')
var http= require('http')
var Url= require('url')
var request = require('request');
var connect= require('connect')
var async= require('async')
var _= require('underscore')
var ComposableMiddleware= require('composable-middleware')

var port= 8889
var host = 'localhost'

var FSRoute = require( '..' );

// The sample tree from README.md
var ReadmeTree= {
  '*':function(descend) {
    var res= this.res
    var stack= this.stack= ['/']
    var svsend= res.send
    res.send= function(msg) {
      svsend.call(res,stack.join(':')+':'+msg)
    }
    descend()
  },
  'foo.':function(descend) {
    this.res.send('in foo.')
  },
  'foo._DELETE':function(descend) {
    this.res.send('in DELETE foo.')
  },
  foo:{
    '*':function(descend) {
      this.stack.push('foo')
      descend()
    },
    '/':function(descend) {
      this.res.send('in foo/')
    },
    '/._DELETE':function(descend) {
      this.res.send('in DELETE foo/')
    },
    'bar._GET': function(descend) {
      this.res.send('in GET foo/bar')
    },
    'bar._POST': function(descend) {
      this.res.send('in POST foo/bar')
    },
    bar: function(descend) {
      this.res.send('in foo/bar')
    },
    'bar.json._GET': function(descend) {
      this.res.send('in GET foo/bar.json')
    },
    'bar.json': function(descend) {
      this.res.send('in GET foo/bar')
    },
  }
}

function noop ()
{
}

function serve(middleware,requests,done) {
  var server= http.createServer(function(req,res){
    res.send= function(status,body) {
      if (1 == arguments.length) {
        body= status;
        status= 200;
      }
      this.statusCode= status;
      this.end(body);
    }
    middleware(req,res,function(err) {
      if (err) {
        res.statusCode= 500
        res.end(err.toString())
      }
      else {
        res.statusCode= 404
        res.end()
      }
    });
  })
  async.series(requests,function (){
    server.close(function() {
      done();
    });
  })
  server.listen(port)
}

function fullURL(url)
{
  return 'http://localhost:'+port+url;
}

function get(url,expected,done) {
  request.get(fullURL(url), function (error, response, body) {
    response.statusCode.should.equal(200);
    body.should.equal(expected);
    done();
  })
}

function get404(url,done) {
  request.get(fullURL(url), function (error, response, body) {
    response.statusCode.should.equal(404);
    done();
  })
}

function new_router(tree) {
  return new FSRoute(tree)
}

function simple_get_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= new_router(tree)
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(cb) {
        get(url,expected,cb)
      },
    ],
    done
  );
}

function simple_post_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= new_router(tree)
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'POST'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function simple_delete_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= new_router(tree)
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware()
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'DELETE'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function fs_get_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= new_router().add_modules(path.resolve(__dirname,'../testsites/foobar'))
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(cb) {
        get(url,expected,cb)
      },
    ],
    done
  );
}

function fs_post_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= new_router().add_modules(path.resolve(__dirname,'../testsites/foobar'))
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'POST'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function fs_delete_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= new_router().add_modules(path.resolve(__dirname,'../testsites/foobar'))
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware()
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'DELETE'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function readme_get_test(url,expected,done)
{
  simple_get_test(url,expected,ReadmeTree,done)
}

function readme_post_test(url,expected,done)
{
  simple_post_test(url,expected,ReadmeTree,done)
}

function readme_delete_test(url,expected,done)
{
  simple_delete_test(url,expected,ReadmeTree,done)
}

function readme_fs_get_test(url,expected,done)
{
  fs_get_test(url,expected,ReadmeTree,done)
}

function readme_fs_post_test(url,expected,done)
{
  fs_post_test(url,expected,ReadmeTree,done)
}

function readme_fs_delete_test(url,expected,done)
{
  fs_delete_test(url,expected,ReadmeTree,done)
}

describe( 'FSRoute', function() {
  describe( 'FSRoute()', function() {
    it( 'should be a function', function() {
      assert(_.isFunction(FSRoute))
    } );
    it( 'should return a RequestHandler constructor function', function() {
      var fsRouter= new_router()
      assert(_.isFunction(fsRouter.request_handler))
      fsRouter.should.have.property('connect_middleware')
      fsRouter.should.have.property('composable_middleware')
    } );
    it( 'should simply 404 if no handler for the request', function(done) {
      serve(
        new_router().connect_middleware(),
        [
          function(cb) {
            get404('/zzyzx',cb)
          },
        ],
        done
      );
    } );
    it( 'should find code at the top level of the code branch', function(done) {
      var tree= {
        hello: function (next) {
          this.res.end('world')
        }
      }
      simple_get_test('/hello','world',tree,done);
    } );
    it( 'should support two simulaneous routers', function(done) {
      var fsRouter= new_router({
        foo: function (next) {
                this.res.end('bar')
              }
      })
      var fsRouter2= new_router({
        a: function (next) {
                this.res.end('b')
              }
      })

      serve(
        new ComposableMiddleware(
          function(next) {
            next()
          },
          fsRouter.composable_middleware(),
          fsRouter2.composable_middleware()
        ),
        [
          function(cb) {
            get('/foo','bar',cb)
          },
          function(cb) {
            get('/a','b',cb)
          },
        ],
        done
      );
    } );
    it( 'should support two simulaneous routers and not confuse them', function(done) {
      var fsRouter= new_router({
        foo: function (next) {
                this.res.end('bar')
              }
      })
      var fsRouter2= new_router({
        a: function (next) {
                this.res.end('b')
              }
      })

      serve(
        new ComposableMiddleware(
          function(next) {
            next()
          },
          fsRouter2.composable_middleware()
        ),
        [
          function(cb) {
            get404('/foo',cb)
          },
          function(cb) {
            get('/a','b',cb)
          },
        ],
        done
      );
    } );
    it( 'should support two simulaneous routers and not confuse them. part II', function(done) {
      var fsRouter= new_router({
        foo: function (next) {
                this.res.end('bar')
              }
      })
      var fsRouter2= new_router({
        a: function (next) {
                this.res.end('b')
              }
      })

      serve(
        new ComposableMiddleware(
          function(next) {
            next()
          },
          fsRouter.composable_middleware()
        ),
        [
          function(cb) {
            get('/foo','bar',cb)
          },
          function(cb) {
            get404('/a',cb)
          },
        ],
        done
      );
    } );
    it( 'should serve /foo/bar from the README filesystem sample', function(done) {
      readme_get_test('/foo/bar','/:foo:in GET foo/bar',done);
    } );
    it( 'should serve POST /foo/bar from the README sample', function(done) {
      readme_post_test('/foo/bar','/:foo:in POST foo/bar',done);
    } );
    it( 'should serve /foo/bar.json from the README sample', function(done) {
      readme_get_test('/foo/bar.json','/:foo:in GET foo/bar.json',done);
    } );
    it( 'should serve /foo (foo as function, not object) from the README sample', function(done) {
      readme_get_test('/foo','/:in foo.',done);
    } );
    it( 'should serve DELETE /foo (foo as function, not object) from the README sample', function(done) {
      readme_delete_test('/foo','/:in DELETE foo.',done);
    } );
    it( 'should serve /foo/ from the README sample', function(done) {
      readme_get_test('/foo/','/:foo:in foo/',done);
    } );
    it( 'should serve DELETE /foo/ from the README sample', function(done) {
      readme_delete_test('/foo/','/:foo:in DELETE foo/',done);
    } );
    it( 'should find that foo/baz is not defined, but still triggers the indeterminate / and /foo handlers', function(done) {
      readme_get_test('/foo/baz','/:foo:no determinate handler',done);
    } );

    it( 'should serve /foo/bar from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo/bar','/:fsfoo:in (fs) GET foo/bar',done);
    } );
    it( 'should serve POST /foo/bar from the README filesystem sample', function(done) {
      readme_fs_post_test('/foo/bar','/:fsfoo:in (fs) POST foo/bar',done);
    } );
    it( 'should serve /foo/bar.json from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo/bar.json','/:fsfoo:in (fs) GET foo/bar.json',done);
    } );
    it( 'should serve /foo (foo as function, not object) from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo','/:in (fs) foo.',done);
    } );
    it( 'should serve DELETE /foo (foo as function, not object) from the README filesystem sample', function(done) {
      readme_fs_delete_test('/foo','/:in (fs) DELETE foo.',done);
    } );
    it( 'should serve /foo/ from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo/','/:fsfoo:in (fs) foo/',done);
    } );
    it( 'should serve DELETE /foo/ from the README filesystem sample', function(done) {
      readme_fs_delete_test('/foo/','/:fsfoo:in (fs) DELETE foo/',done);
    } );

    function define_router(map,rootdir)
    {
      var fsr= new FSRoute(map)
      if (rootdir) {
        fsr.add_modules(path.resolve(__dirname,rootdir))
      }
      return fsr;
    }
    function pseudo_server(router,method,url,on_send,on_404)
    {
      router.request_handler({
        req: {
          method: method,
          url: url
        },
        res: {
          send: on_send
        }
      }, function () {
        if (on_404) on_404()
        else throw new Error('404')
      })
    }
    it( 'should merge tree and filesystem functions', function(done) {
      var fsr= define_router(
        {
          foo: {bah: function(descend) {
            this.res.send('foobah!')
          }}
        },
        '../testsites/foobar'
      )
      pseudo_server(fsr,'GET','/foo/bah',
        function(msg) {
          msg.should.equal('/:fsfoo:foobah!')
          done()
        }
      )
    } );

  } );
} );
<commit_msg>Test function define_router replaces new_router<commit_after>
'use strict';

var should= require('should')
var assert= require('assert')
var path= require('path')
var http= require('http')
var Url= require('url')
var request = require('request');
var connect= require('connect')
var async= require('async')
var _= require('underscore')
var ComposableMiddleware= require('composable-middleware')

var port= 8889
var host = 'localhost'

var FSRoute = require( '..' );

// The sample tree from README.md
var ReadmeTree= {
  '*':function(descend) {
    var res= this.res
    var stack= this.stack= ['/']
    var svsend= res.send
    res.send= function(msg) {
      svsend.call(res,stack.join(':')+':'+msg)
    }
    descend()
  },
  'foo.':function(descend) {
    this.res.send('in foo.')
  },
  'foo._DELETE':function(descend) {
    this.res.send('in DELETE foo.')
  },
  foo:{
    '*':function(descend) {
      this.stack.push('foo')
      descend()
    },
    '/':function(descend) {
      this.res.send('in foo/')
    },
    '/._DELETE':function(descend) {
      this.res.send('in DELETE foo/')
    },
    'bar._GET': function(descend) {
      this.res.send('in GET foo/bar')
    },
    'bar._POST': function(descend) {
      this.res.send('in POST foo/bar')
    },
    bar: function(descend) {
      this.res.send('in foo/bar')
    },
    'bar.json._GET': function(descend) {
      this.res.send('in GET foo/bar.json')
    },
    'bar.json': function(descend) {
      this.res.send('in GET foo/bar')
    },
  }
}

function noop ()
{
}

function define_router(tree,rootdir)
{
  if (arguments.length == 1 && _.isString(tree)) {
    rootdir= tree;
    tree= {};
  }
  var fsr= new FSRoute(tree)
  if (rootdir) {
    fsr.add_modules(path.resolve(__dirname,rootdir))
  }
  return fsr;
}

function serve(middleware,requests,done) {
  var server= http.createServer(function(req,res){
    res.send= function(status,body) {
      if (1 == arguments.length) {
        body= status;
        status= 200;
      }
      this.statusCode= status;
      this.end(body);
    }
    middleware(req,res,function(err) {
      if (err) {
        res.statusCode= 500
        res.end(err.toString())
      }
      else {
        res.statusCode= 404
        res.end()
      }
    });
  })
  async.series(requests,function (){
    server.close(function() {
      done();
    });
  })
  server.listen(port)
}

function fullURL(url)
{
  return 'http://localhost:'+port+url;
}

function get(url,expected,done) {
  request.get(fullURL(url), function (error, response, body) {
    response.statusCode.should.equal(200);
    body.should.equal(expected);
    done();
  })
}

function get404(url,done) {
  request.get(fullURL(url), function (error, response, body) {
    response.statusCode.should.equal(404);
    done();
  })
}

function simple_get_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= define_router(tree)
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(cb) {
        get(url,expected,cb)
      },
    ],
    done
  );
}

function simple_post_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= define_router(tree)
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'POST'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function simple_delete_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= define_router(tree)
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware()
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'DELETE'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function fs_get_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= define_router('../testsites/foobar')
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(cb) {
        get(url,expected,cb)
      },
    ],
    done
  );
}

function fs_post_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= define_router('../testsites/foobar')
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware(),
      function(next) {
        if (this.stack) this.res.send('no determinate handler')
        else next()
      }
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'POST'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function fs_delete_test(url,expected,tree,done)
{
  if (arguments.length == 3) {
    done= tree;
    tree= undefined;
  }
  var fsRouter= define_router('../testsites/foobar')
  serve(
    ComposableMiddleware(
      fsRouter.composable_middleware()
    ),
    [
      function(done) {
        var options= Url.parse(fullURL(url))
        options.method= 'DELETE'
        var req= http.request(options, function (res) {
          res.statusCode.should.equal(200);
          var s= ''
          res.on('error',function(e) {
            throw e;
          })
          res.on('data',function(chunk) {
            s+= chunk
          })
          res.on('end', function(){
            s.should.equal(expected);
            done();
          })
        })
        req.end()
      },
    ],
    done
  );
}

function readme_get_test(url,expected,done)
{
  simple_get_test(url,expected,ReadmeTree,done)
}

function readme_post_test(url,expected,done)
{
  simple_post_test(url,expected,ReadmeTree,done)
}

function readme_delete_test(url,expected,done)
{
  simple_delete_test(url,expected,ReadmeTree,done)
}

function readme_fs_get_test(url,expected,done)
{
  fs_get_test(url,expected,ReadmeTree,done)
}

function readme_fs_post_test(url,expected,done)
{
  fs_post_test(url,expected,ReadmeTree,done)
}

function readme_fs_delete_test(url,expected,done)
{
  fs_delete_test(url,expected,ReadmeTree,done)
}

describe( 'FSRoute', function() {
  describe( 'FSRoute()', function() {
    it( 'should be a function', function() {
      assert(_.isFunction(FSRoute))
    } );
    it( 'should return a RequestHandler constructor function', function() {
      var fsRouter= define_router()
      assert(_.isFunction(fsRouter.request_handler))
      fsRouter.should.have.property('connect_middleware')
      fsRouter.should.have.property('composable_middleware')
    } );
    it( 'should simply 404 if no handler for the request', function(done) {
      serve(
        define_router().connect_middleware(),
        [
          function(cb) {
            get404('/zzyzx',cb)
          },
        ],
        done
      );
    } );
    it( 'should find code at the top level of the code branch', function(done) {
      var tree= {
        hello: function (next) {
          this.res.end('world')
        }
      }
      simple_get_test('/hello','world',tree,done);
    } );
    it( 'should support two simulaneous routers', function(done) {
      var fsRouter= define_router({
        foo: function (next) {
                this.res.end('bar')
              }
      })
      var fsRouter2= define_router({
        a: function (next) {
                this.res.end('b')
              }
      })

      serve(
        new ComposableMiddleware(
          function(next) {
            next()
          },
          fsRouter.composable_middleware(),
          fsRouter2.composable_middleware()
        ),
        [
          function(cb) {
            get('/foo','bar',cb)
          },
          function(cb) {
            get('/a','b',cb)
          },
        ],
        done
      );
    } );
    it( 'should support two simulaneous routers and not confuse them', function(done) {
      var fsRouter= define_router({
        foo: function (next) {
                this.res.end('bar')
              }
      })
      var fsRouter2= define_router({
        a: function (next) {
                this.res.end('b')
              }
      })

      serve(
        new ComposableMiddleware(
          function(next) {
            next()
          },
          fsRouter2.composable_middleware()
        ),
        [
          function(cb) {
            get404('/foo',cb)
          },
          function(cb) {
            get('/a','b',cb)
          },
        ],
        done
      );
    } );
    it( 'should support two simulaneous routers and not confuse them. part II', function(done) {
      var fsRouter= define_router({
        foo: function (next) {
                this.res.end('bar')
              }
      })
      var fsRouter2= define_router({
        a: function (next) {
                this.res.end('b')
              }
      })

      serve(
        new ComposableMiddleware(
          function(next) {
            next()
          },
          fsRouter.composable_middleware()
        ),
        [
          function(cb) {
            get('/foo','bar',cb)
          },
          function(cb) {
            get404('/a',cb)
          },
        ],
        done
      );
    } );
    it( 'should serve /foo/bar from the README filesystem sample', function(done) {
      readme_get_test('/foo/bar','/:foo:in GET foo/bar',done);
    } );
    it( 'should serve POST /foo/bar from the README sample', function(done) {
      readme_post_test('/foo/bar','/:foo:in POST foo/bar',done);
    } );
    it( 'should serve /foo/bar.json from the README sample', function(done) {
      readme_get_test('/foo/bar.json','/:foo:in GET foo/bar.json',done);
    } );
    it( 'should serve /foo (foo as function, not object) from the README sample', function(done) {
      readme_get_test('/foo','/:in foo.',done);
    } );
    it( 'should serve DELETE /foo (foo as function, not object) from the README sample', function(done) {
      readme_delete_test('/foo','/:in DELETE foo.',done);
    } );
    it( 'should serve /foo/ from the README sample', function(done) {
      readme_get_test('/foo/','/:foo:in foo/',done);
    } );
    it( 'should serve DELETE /foo/ from the README sample', function(done) {
      readme_delete_test('/foo/','/:foo:in DELETE foo/',done);
    } );
    it( 'should find that foo/baz is not defined, but still triggers the indeterminate / and /foo handlers', function(done) {
      readme_get_test('/foo/baz','/:foo:no determinate handler',done);
    } );

    it( 'should serve /foo/bar from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo/bar','/:fsfoo:in (fs) GET foo/bar',done);
    } );
    it( 'should serve POST /foo/bar from the README filesystem sample', function(done) {
      readme_fs_post_test('/foo/bar','/:fsfoo:in (fs) POST foo/bar',done);
    } );
    it( 'should serve /foo/bar.json from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo/bar.json','/:fsfoo:in (fs) GET foo/bar.json',done);
    } );
    it( 'should serve /foo (foo as function, not object) from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo','/:in (fs) foo.',done);
    } );
    it( 'should serve DELETE /foo (foo as function, not object) from the README filesystem sample', function(done) {
      readme_fs_delete_test('/foo','/:in (fs) DELETE foo.',done);
    } );
    it( 'should serve /foo/ from the README filesystem sample', function(done) {
      readme_fs_get_test('/foo/','/:fsfoo:in (fs) foo/',done);
    } );
    it( 'should serve DELETE /foo/ from the README filesystem sample', function(done) {
      readme_fs_delete_test('/foo/','/:fsfoo:in (fs) DELETE foo/',done);
    } );

    function pseudo_server(router,method,url,on_send,on_404)
    {
      router.request_handler({
        req: {
          method: method,
          url: url
        },
        res: {
          send: on_send
        }
      }, function () {
        if (on_404) on_404()
        else throw new Error('404')
      })
    }
    it( 'should merge tree and filesystem functions', function(done) {
      var fsr= define_router(
        {
          foo: {bah: function(descend) {
            this.res.send('foobah!')
          }}
        },
        '../testsites/foobar'
      )
      pseudo_server(fsr,'GET','/foo/bah',
        function(msg) {
          msg.should.equal('/:fsfoo:foobah!')
          done()
        }
      )
    } );

  } );
} );
<|endoftext|>


<commit_before>var keystone = require('../../'),
	_ = require('underscore'),
	async = require('async'),
	cloudinary = require('cloudinary'),
	moment = require('moment'),
	utils = require('keystone-utils');

exports = module.exports = function(req, res) {

	req.list.model.findById(req.params.item).exec(function(err, item) {

		if (Array.isArray(item))
			item = item[0]; // WTF??? I thought findById was only meant to return a single document.

		if (!item) {
			req.flash('error', 'Item ' + req.params.item + ' could not be found.');
			return res.redirect('/keystone/' + req.list.path);
		}

		var viewLocals = {
			validationErrors: {}
		};

		var renderView = function() {

			var relationships = _.values(_.compact(_.map(req.list.relationships, function(i) {
				if (i.isValid) {
					return _.clone(i);
				} else {
				    keystone.console.err('Relationship Configuration Error', 'Relationship: ' + i.path + ' on list: ' + req.list.key + ' links to an invalid list: ' + i.ref);
					return null;
				}
			})));

			var drilldown = {
				def: req.list.get('drilldown'),
				data: {},
				items: []
			};

			var loadDrilldown = function(cb) {

				if (!drilldown.def)
					return cb();

				// step back through the drilldown list and load in reverse order to support nested relationships
				// TODO: proper support for nested relationships in drilldown
				drilldown.def = drilldown.def.split(' ').reverse();

				async.eachSeries(drilldown.def, function(path, done) {

					var field = req.list.fields[path];

					if (!field || field.type != 'relationship')
						throw new Error('Drilldown for ' + req.list.key + ' is invalid: field at path ' + path + ' is not a relationship.');

					var refList = field.refList;

					if (field.many) {
						if (!item.get(field.path).length) {
							return done();
						}
						refList.model.find().where('_id').in(item.get(field.path)).limit(4).exec(function(err, results) {
							if (err || !results) {
								done(err);
							}
							var more = (results.length == 4) ? results.pop() : false;
							if (results.length) {
								drilldown.data[path] = results;
								drilldown.items.push({
									list: refList,
									items: _.map(results, function(i) { return {
										label: refList.getDocumentName(i),
										href: '/keystone/' + refList.path + '/' + i.id
									}}),
									more: (more) ? true : false
								});
							}
							done();
						});
					} else {
						if (!item.get(field.path)) {
							return done();
						}
						refList.model.findById(item.get(field.path)).exec(function(err, result) {
							if (result) {
								drilldown.data[path] = result;
								drilldown.items.push({
									list: refList,
									label: refList.getDocumentName(result),
									href: '/keystone/' + refList.path + '/' + result.id
								});
							}
							done();
						});
					}

				}, function(err) {
					// put the drilldown list back in the right order
					drilldown.def.reverse();
					drilldown.items.reverse();
					cb(err);
				});
			}

			var loadRelationships = function(cb) {

				async.each(relationships, function(rel, done) {

					// TODO: Handle invalid relationship config
					rel.list = keystone.list(rel.ref);
					rel.sortable = (rel.list.get('sortable') && rel.list.get('sortContext') == req.list.key + ':' + rel.path);

					// TODO: Handle relationships with more than 1 page of results
					var q = rel.list.paginate({ page: 1, perPage: 100 })
						.where(rel.refPath).equals(item.id)
						.sort(rel.list.defaultSort);

					// rel.columns = _.reject(rel.list.defaultColumns, function(col) { return (col.type == 'relationship' && col.refList == req.list) });
					rel.columns = rel.list.defaultColumns
					rel.list.selectColumns(q, rel.columns);

					q.exec(function(err, results) {
						rel.items = results;
						done(err);
					});

				}, cb);
			}
			var	loadFormFieldTemplates = function(cb){
				var onlyFields = function(item) { return item.type == 'field'; }
				var compile = function(item, callback) { item.field.compile('form',callback); }
				async.eachSeries(req.list.uiElements.filter(onlyFields), compile , cb);
			}
			/** Render View */

			async.parallel([
				loadDrilldown,
				loadRelationships,
				loadFormFieldTemplates
			], function(err) {

				var showRelationships = _.some(relationships, function(rel) {
					return rel.items.results.length;
				});

				keystone.render(req, res, 'item', _.extend(viewLocals, {
					section: keystone.nav.by.list[req.list.key] || {},
					title: 'Keystone: ' + req.list.singular + ': ' + req.list.getDocumentName(item),
					page: 'item',
					list: req.list,
					item: item,
					relationships: relationships,
					showRelationships: showRelationships,
					drilldown: drilldown
				}));

			});

		}

		if (req.method == 'POST' && req.body.action == 'updateItem' && !req.list.get('noedit')) {

			item.getUpdateHandler(req).process(req.body, { flashErrors: true, logErrors: true }, function(err) {
				if (err) {
					return renderView();
				}
				req.flash('success', 'Your changes have been saved.');
				return res.redirect('/keystone/' + req.list.path + '/' + item.id);
			});

		} else {
			renderView();
		}

	});

}
<commit_msg>Cleanup<commit_after>var keystone = require('../../'),
	_ = require('underscore'),
	async = require('async'),
	cloudinary = require('cloudinary'),
	moment = require('moment'),
	utils = require('keystone-utils');

exports = module.exports = function(req, res) {

	req.list.model.findById(req.params.item).exec(function(err, item) {

		if (Array.isArray(item)) {
			item = item[0]; // WTF??? I thought findById was only meant to return a single document.
		}

		if (!item) {
			req.flash('error', 'Item ' + req.params.item + ' could not be found.');
			return res.redirect('/keystone/' + req.list.path);
		}

		var viewLocals = {
			validationErrors: {}
		};

		var renderView = function() {

			var relationships = _.values(_.compact(_.map(req.list.relationships, function(i) {
				if (i.isValid) {
					return _.clone(i);
				} else {
				    keystone.console.err('Relationship Configuration Error', 'Relationship: ' + i.path + ' on list: ' + req.list.key + ' links to an invalid list: ' + i.ref);
					return null;
				}
			})));

			var drilldown = {
				def: req.list.get('drilldown'),
				data: {},
				items: []
			};

			var loadDrilldown = function(cb) {

				if (!drilldown.def)
					return cb();

				// step back through the drilldown list and load in reverse order to support nested relationships
				// TODO: proper support for nested relationships in drilldown
				drilldown.def = drilldown.def.split(' ').reverse();

				async.eachSeries(drilldown.def, function(path, done) {

					var field = req.list.fields[path];

					if (!field || field.type != 'relationship')
						throw new Error('Drilldown for ' + req.list.key + ' is invalid: field at path ' + path + ' is not a relationship.');

					var refList = field.refList;

					if (field.many) {
						if (!item.get(field.path).length) {
							return done();
						}
						refList.model.find().where('_id').in(item.get(field.path)).limit(4).exec(function(err, results) {
							if (err || !results) {
								done(err);
							}
							var more = (results.length == 4) ? results.pop() : false;
							if (results.length) {
								drilldown.data[path] = results;
								drilldown.items.push({
									list: refList,
									items: _.map(results, function(i) { return {
										label: refList.getDocumentName(i),
										href: '/keystone/' + refList.path + '/' + i.id
									}}),
									more: (more) ? true : false
								});
							}
							done();
						});
					} else {
						if (!item.get(field.path)) {
							return done();
						}
						refList.model.findById(item.get(field.path)).exec(function(err, result) {
							if (result) {
								drilldown.data[path] = result;
								drilldown.items.push({
									list: refList,
									label: refList.getDocumentName(result),
									href: '/keystone/' + refList.path + '/' + result.id
								});
							}
							done();
						});
					}

				}, function(err) {
					// put the drilldown list back in the right order
					drilldown.def.reverse();
					drilldown.items.reverse();
					cb(err);
				});
			}

			var loadRelationships = function(cb) {

				async.each(relationships, function(rel, done) {

					// TODO: Handle invalid relationship config
					rel.list = keystone.list(rel.ref);
					rel.sortable = (rel.list.get('sortable') && rel.list.get('sortContext') == req.list.key + ':' + rel.path);

					// TODO: Handle relationships with more than 1 page of results
					var q = rel.list.paginate({ page: 1, perPage: 100 })
						.where(rel.refPath).equals(item.id)
						.sort(rel.list.defaultSort);

					// rel.columns = _.reject(rel.list.defaultColumns, function(col) { return (col.type == 'relationship' && col.refList == req.list) });
					rel.columns = rel.list.defaultColumns
					rel.list.selectColumns(q, rel.columns);

					q.exec(function(err, results) {
						rel.items = results;
						done(err);
					});

				}, cb);
			}

			var	loadFormFieldTemplates = function(cb){
				var onlyFields = function(item) { return item.type == 'field'; }
				var compile = function(item, callback) { item.field.compile('form',callback); }
				async.eachSeries(req.list.uiElements.filter(onlyFields), compile , cb);
			}

			/** Render View */

			async.parallel([
				loadDrilldown,
				loadRelationships,
				loadFormFieldTemplates
			], function(err) {

				var showRelationships = _.some(relationships, function(rel) {
					return rel.items.results.length;
				});

				keystone.render(req, res, 'item', _.extend(viewLocals, {
					section: keystone.nav.by.list[req.list.key] || {},
					title: 'Keystone: ' + req.list.singular + ': ' + req.list.getDocumentName(item),
					page: 'item',
					list: req.list,
					item: item,
					relationships: relationships,
					showRelationships: showRelationships,
					drilldown: drilldown
				}));

			});

		}

		if (req.method == 'POST' && req.body.action == 'updateItem' && !req.list.get('noedit')) {

			item.getUpdateHandler(req).process(req.body, { flashErrors: true, logErrors: true }, function(err) {
				if (err) {
					return renderView();
				}
				req.flash('success', 'Your changes have been saved.');
				return res.redirect('/keystone/' + req.list.path + '/' + item.id);
			});

		} else {
			renderView();
		}

	});

}
<|endoftext|>


<commit_before>// Load our dependencies
var gmsmith = require('../lib/gmsmith');
var spritesmithEngineTest = require('spritesmith-engine-test');

// Configure gmsmith for our environment
// DEV: In case it recurs, we had downcasting of imagemagick's spritesheets to 8 bit for `get-pixels` loading
//   See https://github.com/twolfson/gmsmith/blob/0.4.3/test/gmsmith_test_content.js#L39
var engineOptions = {};
if (process.env.TEST_IMAGEMAGICK === 'TRUE') {
  engineOptions.imagemagick = true;
}

// Run our tests
spritesmithEngineTest.run({
  engine: gmsmith,
  engineName: 'gmsmith',
  // TODO: Be sure to add support for this in `spritesmith-engine-test`
  engineOptions: engineOptions,
  tests: {
    // DEV: When we have exactly 1 png, it seems to darken the entire canvas
    //   Thankfully we still have a test for multiple png images which passes
    renderPngCanvas: false,
    renderMultiplePngImages: true,
    // DEV: JPG seems to change significantly on Travis CI for IMAGEMAGICK, disable those tests for now
    renderJpgCanvas: !(process.env.TRAVIS && process.env.TEST_IMAGEMAGICK),
    renderGifCanvas: false,
    // If we are on Windows, skip over performance test (it cannot handle the long argument string)
    renderManyPngImages: process.platform === 'win32'
  }
});
<commit_msg>Fixed up TODO<commit_after>// Load our dependencies
var gmsmith = require('../lib/gmsmith');
var spritesmithEngineTest = require('spritesmith-engine-test');

// Configure gmsmith for our environment
// DEV: In case it recurs, we had downcasting of imagemagick's spritesheets to 8 bit for `get-pixels` loading
//   See https://github.com/twolfson/gmsmith/blob/0.4.3/test/gmsmith_test_content.js#L39
var engineOptions = {};
if (process.env.TEST_IMAGEMAGICK === 'TRUE') {
  engineOptions.imagemagick = true;
}

// Run our tests
spritesmithEngineTest.run({
  engine: gmsmith,
  engineName: 'gmsmith',
  engineOptions: engineOptions,
  tests: {
    // DEV: When we have exactly 1 png, it seems to darken the entire canvas
    //   Thankfully we still have a test for multiple png images which passes
    renderPngCanvas: false,
    renderMultiplePngImages: true,
    // DEV: JPG seems to change significantly on Travis CI for IMAGEMAGICK, disable those tests for now
    renderJpgCanvas: !(process.env.TRAVIS && process.env.TEST_IMAGEMAGICK),
    renderGifCanvas: false,
    // If we are on Windows, skip over performance test (it cannot handle the long argument string)
    renderManyPngImages: process.platform === 'win32'
  }
});
<|endoftext|>


<commit_before>(function(ns) {
  var prevState = ns.prevState || [];
  var currentState = [];
  var domGUID = ns.domGUID || 0;
  var nodeTypes = [];

  $('body').contents().each(function processNodes() {
    var jqThis = $(this);
    var val = "";
    var childs = [];
    var thisUID = -1;

    if(this.domDeltaID) { thisUID = this.domDeltaID; }
    else {
      thisUID = this.domDeltaID = ns.domGUID++;
      val += "ADDED NODE: " + this.domDeltaID + "; ";
    }

    if(!(this.nodeType in nodeTypes)) nodeTypes.push(this.nodeType);

    if(jqThis.contents().length > 0) {
      // Make sure children are processed first so we can get a UID for each of them
      jqThis.contents().each(processNodes);

      jqThis.contents().each(function() {
        childs.push(this);
      });
    }

    currentState[thisUID] = {value: val, children:childs};
  });

  console.log("NodeTypes", nodeTypes);
  ns.prevState = currentState;
  ns.domGUID = domGUID;

})(window.top);
<commit_msg>test correction<commit_after>(function(ns) {
  var prevState = ns.prevState || [];
  var currentState = [];
  var domGUID = ns.domGUID || 0;
  var nodeTypes = [];

  $('body').contents().each(function processNodes() {
    var jqThis = $(this);
    var val = "";
    var childs = [];
    var thisUID = -1;

    if(this.domDeltaID) { thisUID = this.domDeltaID; }
    else {
      thisUID = this.domDeltaID = ns.domGUID++;
      val += "ADDED NODE: " + this.domDeltaID + "; ";
    }

    if(nodeTypes.indexOf(this.nodeType) >= 0) nodeTypes.push(this.nodeType);

    if(jqThis.contents().length > 0) {
      // Make sure children are processed first so we can get a UID for each of them
      jqThis.contents().each(processNodes);

      jqThis.contents().each(function() {
        childs.push(this);
      });
    }

    currentState[thisUID] = {value: val, children:childs};
  });

  console.log("NodeTypes", nodeTypes);
  ns.prevState = currentState;
  ns.domGUID = domGUID;

})(window.top);
<|endoftext|>


<commit_before>// DELETE /db/{db}/groups/{id}
// Groups / Delete a group
//
// Alpha @ 0.1.23
//
// `202 Accepted`,
// [Delayed](/docs/#/concept/delayed.md).
// [Idempotent](/docs/#/concept/idempotent.md).
//
// Deletes a group forever. The contacts remain in the database, but their
// membership (to the deleted group) is forgotten.
//
// ### Response format
//     { "at": <clock> }
//

TODO("The response has valid return code and content type.", function(next) {
    Assert.fail();
});

// # Examples
//
// ### Example request
//     DELETE /db/0SNQc00211H/groups/0SNQe0032JZ
//
// ### Example response
//     202 Accepted
//     Content-Type: application/json
//
//     { "at": [[1, 334]] }

TODO("Deleted group disappears.", function(next) {
    Assert.fail();
});

// # Errors
//
// ## Returns `404 Not Found`
// - ... if database `{db}` does not exist

TODO("Returns 404 when database does not exist.", function(next) {
    Assert.fail();
});

// - ... if group `{id}` does not exist in database `{db}`

TODO("Returns 404 when group does not exist.", function(next) {
    Assert.fail();
});

// ## Returns `401 Unauthorized`
// - ... if the provided token does not grant access to all contacts,
//   or no token was provided

TODO("Returns 401 when token is not valid.", function(next) {
    Assert.fail();
});

// ## Returns `403 Forbidden`
// - ... when attempting to delete group [`admin`](/docs/#/group/admin.md).

TODO("Returns 403 when deleting admin group.", function(next) {
    Assert.fail();
});

// # Access restrictions
//
// Currently, anyone can delete a group with a token for the corresponding database.
// This is subject to change in future versions.
<commit_msg>Write tests for DELETE /groups/{id}<commit_after>// DELETE /db/{db}/groups/{id}
// Groups / Delete a group
//
// Alpha @ 0.1.23
//
// `202 Accepted`,
// [Delayed](/docs/#/concept/delayed.md).
// [Idempotent](/docs/#/concept/idempotent.md).
//
// Deletes a group forever. The people remain in the database, but their
// membership (to the deleted group) is forgotten.
//
// ### Response format
//     { "at": <clock> }
//

TEST("The response has valid return code and content type.", function(Query) {

    var db = Query.mkdb();
    var auth = Query.auth(db);

    var id = Query.post(["db/",db,"/groups"],{},auth).id();

    return Query.del(["db/",db,"/groups/",id],auth)
	.assertStatus(202).assertIsJson();

});

// # Examples
//
// ### Example request
//     DELETE /db/0SNQc00211H/groups/0SNQe0032JZ
//
// ### Example response
//     202 Accepted
//     Content-Type: application/json
//
//     { "at": [[1, 334]] }

TEST("Deleted group disappears.", function(Query) {

    var db = Query.mkdb();
    var auth = Query.auth(db);

    var id = Query.post(["db/",db,"/groups"],{},auth).id();

    return Query.del(["db/",db,"/groups/",id],auth).then(function() {
	return Query.get(["db/",db,"/groups/",id],auth).assertStatus(404);
    });

});

// # Errors
//
// ## Returns `401 Unauthorized`
// - ... if the provided token does not allow acting as `{as}`.

TEST("Returns 401 when token is not valid.", function(Query) {
    var db = Query.mkdb();
    return Query.del(["db/",db,"/groups/00000000000"],{id:"01234567890",token:"01234567890"})
	.assertStatus(401);
});

// ## Returns `403 Forbidden`
// - ... when attempting to delete a group without having **admin** access
//   to that group.

TEST("Returns 403 when deleting group without admin access.", function(Query) {
    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/groups"],{"audience":{"moderate":"anyone"}},auth).id();
    var peon = Query.auth(db,false,"peon@runorg.com")
    return Query.del(["db/",db,"/groups/",id],peon).assertStatus(403);
});

// - ... when attempting to delete group [`admin`](/docs/#/group/admin.md).

TEST("Returns 403 when deleting admin group.", function(Query) {
    var db = Query.mkdb();
    return Query.del(["db/",db,"/groups/admin"]).assertStatus(403);
});

// ## Returns `404 Not Found`
// - ... if database `{db}` does not exist

TEST("Returns 404 when database does not exist.", function(Query) {
    return Query.del(["db/00000000000/groups/00000000000"])
	.assertStatus(404);
});

// - ... if group `{id}` does not exist in database `{db}`

TEST("Returns 404 when group does not exist.", function(Query) {
    var db = Query.mkdb();
    return Query.del(["db/",db,"/groups/00000000000"])
	.assertStatus(404);
});

// # Access restrictions
//
// Deleting a group requires **admin** [access level](/docs/#/group/audience.js)
// to that group. The [`admin`](/docs/#/group/admin.md) group may not be deleted.<|endoftext|>