<commit_before>/**
 * Process overview data
 */

import { PAGES, AVERAGE_MEDIAN, MONTHS_SHORT, OVERVIEW_COLUMNS } from '../../misc/const';
import { FUTURE_INVESTMENT_RATE } from '../../misc/config';
import { yearMonthDifference } from '../../misc/date';
import { getKeyFromYearMonth, getYearMonthFromKey, listAverage, randnBm } from '../../misc/data';
import { getOverviewCategoryColor, getOverviewScoreColor } from '../../misc/color';
import { List as list, Map as map, fromJS } from 'immutable';

/**
 * Calculate futures from past averages / predictions
 * @param {list} cost: processed cost data
 * @param {list} futureCategories: categories to process
 * @param {integer} futureMonths: number of months going into the future
 * @param {integer} futureKey: key to separate between past/present and future
 * @returns {list} first six columns of data for overview table
 */
const calculateFutures = (cost, futureCategories, futureMonths, futureKey) => {
  return futureCategories.map(category => {
    const categoryCost = cost.get(category);

    if (category === 'funds') {
      // randomly generate fund income projections
      const oldOffset = categoryCost.size - cost.get('balance').size;
      let Xt = categoryCost.get(oldOffset + futureKey - 1);
      return categoryCost.slice(oldOffset, oldOffset + futureKey).concat(list(
        Array.apply(null, new Array(categoryCost.size - oldOffset - futureKey)).map(() => {
          Xt *= (1 + FUTURE_INVESTMENT_RATE / 12 + randnBm() / 100);
          return Math.round(Xt);
        })
      ));
    }

    // find the average value and make predictions based on that
    const average = Math.round(listAverage(categoryCost, futureMonths, AVERAGE_MEDIAN));
    const newCost = categoryCost.slice(
      0, categoryCost.size - futureMonths
    ).concat(
      list(Array.apply(null, new Array(futureMonths)).map(() => average))
    );
    return newCost;
  });
};

/**
 * Calculate the remaining table data, e.g. net income
 * Called after calculateFutures() which predicts future data
 * @param {map} data: processed data
 * @returns {list} all twelve columns of data for overview table
 */
const calculateTableData = data => {
  const cost = data.get('cost');
  const numRows = data.get('numRows');
  const startYear = data.get('startYearMonth')[0];
  const startMonth = data.get('startYearMonth')[1];

  // add month column
  const months = list(Array.apply(null, new Array(numRows)).map((_, key) => {
    const yearMonth = getYearMonthFromKey(key, startYear, startMonth);
    return `${MONTHS_SHORT[yearMonth[1] - 1]}-${yearMonth[0]}`;
  }));

  return list.of()
  .push(months)
  .push(cost.get('funds'))
  .push(cost.get('bills'))
  .push(cost.get('food'))
  .push(cost.get('general'))
  .push(cost.get('holiday'))
  .push(cost.get('social'))
  .push(cost.get('income'))
  .push(cost.get('spending'))
  .push(cost.get('net'))
  .push(cost.get('predicted'))
  .push(cost.get('balance'));
};

export const rProcessDataOverview = (costMap, startYearMonth, endYearMonth, currentYearMonth, futureMonths) => {
  const numRows = yearMonthDifference(startYearMonth, endYearMonth) + 1;
  const numCols = 1;
  const yearMonths = Array.apply(null, new Array(numRows)).map((_, key) => {
    return getYearMonthFromKey(key, startYearMonth[0], startYearMonth[1]);
  });
  const yearMonthsList = list(yearMonths);

  // separate funds into old and displayed
  const funds = costMap.get('funds');
  let cost = costMap.set('funds', funds.slice(-numRows))
  .set('fundsOld', funds.slice(0, funds.size - numRows));

  const futureCategories = list.of('funds', 'food', 'general', 'holiday', 'social');
  const futureKey = yearMonthDifference(startYearMonth, currentYearMonth) + 1;
  const futureData = calculateFutures(cost, futureCategories, futureMonths, futureKey);
  futureCategories.forEach((category, key) => {
    cost = cost.set(category, futureData.get(key));
  });

  // add spending column
  const spending = yearMonthsList.map((month, key) => {
    return cost.getIn(['bills', key]) +
      cost.getIn(['food', key]) +
      cost.getIn(['general', key]) +
      cost.getIn(['holiday', key]) +
      cost.getIn(['social', key]);
  });

  // add net cash flow column
  const net = yearMonthsList.map((month, key) => {
    // add predicted (random) fund income to the net cash flow
    const fundIncome = key === 0 || key < futureKey ? 0
      : cost.getIn(['funds', key]) - cost.getIn(['funds', key - 1]);
    return cost.getIn(['income', key]) - spending.get(key) + fundIncome;
  });

  // add predicted balance
  let lastPredicted = cost.getIn(['balance', 0]);
  const predicted = yearMonthsList.map((month, key) => {
    if (key > 0 && (key < futureKey ||
                    (key === futureKey) && cost.getIn(['balance', key - 1]) > 0)) {
      lastPredicted = cost.getIn(['balance', key - 1]) + net.get(key);
      return lastPredicted;
    }
    const newPredicted = lastPredicted + net.get(key);
    lastPredicted = newPredicted;
    return newPredicted;
  });

  const balanceWithPredicted = cost.get('balance').slice(0, futureKey)
  .concat(predicted.slice(-numRows + futureKey));

  cost = cost
  .set('spending', spending)
  .set('net', net)
  .set('predicted', predicted)
  .set('balanceWithPredicted', balanceWithPredicted);

  return map({
    numRows,
    numCols,
    futureKey,
    futureMonths,
    startYearMonth,
    endYearMonth,
    currentYearMonth,
    yearMonths,
    cost
  });
};

/**
 * Process data for insertion into the store
 * @param {object} raw: api JSON data response
 * @returns {Map} immutable data
 */
const rProcessDataOverviewRaw = raw => {
  const currentYearMonth = [raw.currentYear, raw.currentMonth];
  const costMap = fromJS(raw.cost);

  return rProcessDataOverview(
    costMap, raw.startYearMonth, raw.endYearMonth, currentYearMonth, raw.futureMonths);
};

/**
 * Get rows for display in the view
 * @param {List} data: processed data
 * @returns {List} rows for the view
 */
export const rGetOverviewRows = data => {
  const currentYear = data.get('currentYearMonth')[0];
  const currentMonth = data.get('currentYearMonth')[1];

  const tableData = calculateTableData(data);

  // get value ranges and medians for calculating colours
  const values = OVERVIEW_COLUMNS.slice(1).map((column, colKey) => tableData.get(colKey + 1));
  const valueRange = values.map(valuesItem => [valuesItem.min(), valuesItem.max()]);
  const median = values.map(valuesItem => {
    return [
      listAverage(valuesItem.filter(item => item >= 0), 0, AVERAGE_MEDIAN), // median of positive values
      listAverage(valuesItem.filter(item => item < 0), 0, AVERAGE_MEDIAN) // median of negative values
    ];
  });

  const categoryColor = getOverviewCategoryColor();

  // translate the data into table cells for display in the view
  const rows = tableData.get(0).map((monthText, key) => {
    const yearMonth = getYearMonthFromKey(
      key, data.get('startYearMonth')[0], data.get('startYearMonth')[1]);

    const past = yearMonth[0] < currentYear ||
      (yearMonth[0] === currentYear && yearMonth[1] < currentMonth);
    const active = yearMonth[0] === currentYear && yearMonth[1] === currentMonth;
    const future = !past && !active;

    const cells = list(OVERVIEW_COLUMNS).map((column, colKey) => {
      const value = tableData.getIn([colKey, key]);
      let rgb = null;
      if (colKey > 0 && categoryColor[colKey - 1]) {
        rgb = getOverviewScoreColor(
          value, valueRange[colKey - 1], median[colKey - 1], categoryColor[colKey - 1]);
      }
      const editable = column[0] === 'balance';

      return map({
        column,
        value,
        rgb,
        editable
      });
    });

    return map({ cells, past, active, future });
  });

  return rows;
};

/**
 * @function rCalculateOverview
 * @param {Record} reduction: modified reduction
 * @param {integer} pageIndex: page which is modified
 * @param {YMD} newDate: modified item date
 * @param {YMD} oldDate: original item date
 * @param {integer} newItemCost: modified item cost
 * @param {integer} oldItemCost: original item cost
 * @returns {Record} reduction with re-calculated overview data
 */
export const rCalculateOverview = (reduction, pageIndex, newDate, oldDate, newItemCost, oldItemCost) => {
  const overviewKey = PAGES.indexOf('overview');
  const startYearMonth = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'startYearMonth']);

  const newKey = getKeyFromYearMonth(newDate.year, newDate.month, startYearMonth[0], startYearMonth[1]);
  const oldKey = getKeyFromYearMonth(oldDate.year, oldDate.month, startYearMonth[0], startYearMonth[1]);

  const oldCost = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'cost']);
  const numRows = oldCost.get(PAGES[pageIndex]).size;

  // update the changed rows in the overview page
  let newCost = oldCost;
  if (oldKey === newKey) {
    if (oldKey < numRows) {
      newCost = newCost.setIn(
        [PAGES[pageIndex], oldKey],
        oldCost.getIn([PAGES[pageIndex], oldKey]) + newItemCost - oldItemCost
      );
    }
  }
  else {
    if (oldKey < numRows) {
      newCost = newCost.setIn(
        [PAGES[pageIndex], oldKey],
        oldCost.getIn([PAGES[pageIndex], oldKey]) - oldItemCost
      );
    }
    if (newKey < numRows) {
      newCost = newCost.setIn(
        [PAGES[pageIndex], newKey],
        oldCost.getIn([PAGES[pageIndex], newKey]) + newItemCost
      );
    }
  }

  const endYearMonth = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'endYearMonth']);
  const currentYearMonth = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'currentYearMonth']);
  const futureMonths = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'futureMonths']);

  const newData = rProcessDataOverview(
    newCost, startYearMonth, endYearMonth, currentYearMonth, futureMonths);

  return reduction.setIn(['appState', 'pages', overviewKey, 'data'], newData)
  .setIn(['appState', 'pages', overviewKey, 'rows'], rGetOverviewRows(newData));
};

/**
 * Called when data is first loaded
 * @param {Record} reduction: app state
 * @param {integer} pageIndex: page index
 * @param {object} raw: api JSON data
 * @returns {Record} modified reduction
 */
export default (reduction, pageIndex, raw) => {
  const data = rProcessDataOverviewRaw(raw);
  const rows = rGetOverviewRows(data);

  return reduction.setIn(['appState', 'pages', pageIndex], map({ data, rows }));
};

<commit_msg>[bugfix] Don't recalculate old fund values<commit_after>/**
 * Process overview data
 */

import { PAGES, AVERAGE_MEDIAN, MONTHS_SHORT, OVERVIEW_COLUMNS } from '../../misc/const';
import { FUTURE_INVESTMENT_RATE } from '../../misc/config';
import { yearMonthDifference } from '../../misc/date';
import { getKeyFromYearMonth, getYearMonthFromKey, listAverage, randnBm } from '../../misc/data';
import { getOverviewCategoryColor, getOverviewScoreColor } from '../../misc/color';
import { List as list, Map as map, fromJS } from 'immutable';

/**
 * Calculate futures from past averages / predictions
 * @param {list} cost: processed cost data
 * @param {list} futureCategories: categories to process
 * @param {integer} futureMonths: number of months going into the future
 * @param {integer} futureKey: key to separate between past/present and future
 * @returns {list} first six columns of data for overview table
 */
const calculateFutures = (cost, futureCategories, futureMonths, futureKey) => {
  return futureCategories.map(category => {
    const categoryCost = cost.get(category);

    if (category === 'funds') {
      // randomly generate fund income projections
      const oldOffset = categoryCost.size - cost.get('balance').size;
      let Xt = categoryCost.get(oldOffset + futureKey - 1);
      return categoryCost.slice(oldOffset, oldOffset + futureKey).concat(list(
        Array.apply(null, new Array(categoryCost.size - oldOffset - futureKey)).map(() => {
          Xt *= (1 + FUTURE_INVESTMENT_RATE / 12 + randnBm() / 100);
          return Math.round(Xt);
        })
      ));
    }

    // find the average value and make predictions based on that
    const average = Math.round(listAverage(categoryCost, futureMonths, AVERAGE_MEDIAN));
    const newCost = categoryCost.slice(
      0, categoryCost.size - futureMonths
    ).concat(
      list(Array.apply(null, new Array(futureMonths)).map(() => average))
    );
    return newCost;
  });
};

/**
 * Calculate the remaining table data, e.g. net income
 * Called after calculateFutures() which predicts future data
 * @param {map} data: processed data
 * @returns {list} all twelve columns of data for overview table
 */
const calculateTableData = data => {
  const cost = data.get('cost');
  const numRows = data.get('numRows');
  const startYear = data.get('startYearMonth')[0];
  const startMonth = data.get('startYearMonth')[1];

  // add month column
  const months = list(Array.apply(null, new Array(numRows)).map((_, key) => {
    const yearMonth = getYearMonthFromKey(key, startYear, startMonth);
    return `${MONTHS_SHORT[yearMonth[1] - 1]}-${yearMonth[0]}`;
  }));

  return list.of()
  .push(months)
  .push(cost.get('funds'))
  .push(cost.get('bills'))
  .push(cost.get('food'))
  .push(cost.get('general'))
  .push(cost.get('holiday'))
  .push(cost.get('social'))
  .push(cost.get('income'))
  .push(cost.get('spending'))
  .push(cost.get('net'))
  .push(cost.get('predicted'))
  .push(cost.get('balance'));
};

export const rProcessDataOverview = (costMap, startYearMonth, endYearMonth, currentYearMonth, futureMonths) => {
  const numRows = yearMonthDifference(startYearMonth, endYearMonth) + 1;
  const numCols = 1;
  const yearMonths = Array.apply(null, new Array(numRows)).map((_, key) => {
    return getYearMonthFromKey(key, startYearMonth[0], startYearMonth[1]);
  });
  const yearMonthsList = list(yearMonths);

  // separate funds into old and displayed
  let cost = costMap;
  if (!cost.has('fundsOld')) {
    const funds = cost.get('funds');
    cost = cost.set('funds', funds.slice(-numRows))
    .set('fundsOld', funds.slice(0, funds.size - numRows));
  }

  const futureCategories = list.of('funds', 'food', 'general', 'holiday', 'social');
  const futureKey = yearMonthDifference(startYearMonth, currentYearMonth) + 1;
  const futureData = calculateFutures(cost, futureCategories, futureMonths, futureKey);
  futureCategories.forEach((category, key) => {
    cost = cost.set(category, futureData.get(key));
  });

  // add spending column
  const spending = yearMonthsList.map((month, key) => {
    return cost.getIn(['bills', key]) +
      cost.getIn(['food', key]) +
      cost.getIn(['general', key]) +
      cost.getIn(['holiday', key]) +
      cost.getIn(['social', key]);
  });

  // add net cash flow column
  const net = yearMonthsList.map((month, key) => {
    // add predicted (random) fund income to the net cash flow
    const fundIncome = key === 0 || key < futureKey ? 0
      : cost.getIn(['funds', key]) - cost.getIn(['funds', key - 1]);
    return cost.getIn(['income', key]) - spending.get(key) + fundIncome;
  });

  // add predicted balance
  let lastPredicted = cost.getIn(['balance', 0]);
  const predicted = yearMonthsList.map((month, key) => {
    if (key > 0 && (key < futureKey ||
                    (key === futureKey) && cost.getIn(['balance', key - 1]) > 0)) {
      lastPredicted = cost.getIn(['balance', key - 1]) + net.get(key);
      return lastPredicted;
    }
    const newPredicted = lastPredicted + net.get(key);
    lastPredicted = newPredicted;
    return newPredicted;
  });

  const balanceWithPredicted = cost.get('balance').slice(0, futureKey)
  .concat(predicted.slice(-numRows + futureKey));

  cost = cost
  .set('spending', spending)
  .set('net', net)
  .set('predicted', predicted)
  .set('balanceWithPredicted', balanceWithPredicted);

  return map({
    numRows,
    numCols,
    futureKey,
    futureMonths,
    startYearMonth,
    endYearMonth,
    currentYearMonth,
    yearMonths,
    cost
  });
};

/**
 * Process data for insertion into the store
 * @param {object} raw: api JSON data response
 * @returns {Map} immutable data
 */
const rProcessDataOverviewRaw = raw => {
  const currentYearMonth = [raw.currentYear, raw.currentMonth];
  const costMap = fromJS(raw.cost);

  return rProcessDataOverview(
    costMap, raw.startYearMonth, raw.endYearMonth, currentYearMonth, raw.futureMonths);
};

/**
 * Get rows for display in the view
 * @param {List} data: processed data
 * @returns {List} rows for the view
 */
export const rGetOverviewRows = data => {
  const currentYear = data.get('currentYearMonth')[0];
  const currentMonth = data.get('currentYearMonth')[1];

  const tableData = calculateTableData(data);

  // get value ranges and medians for calculating colours
  const values = OVERVIEW_COLUMNS.slice(1).map((column, colKey) => tableData.get(colKey + 1));
  const valueRange = values.map(valuesItem => [valuesItem.min(), valuesItem.max()]);
  const median = values.map(valuesItem => {
    return [
      listAverage(valuesItem.filter(item => item >= 0), 0, AVERAGE_MEDIAN), // median of positive values
      listAverage(valuesItem.filter(item => item < 0), 0, AVERAGE_MEDIAN) // median of negative values
    ];
  });

  const categoryColor = getOverviewCategoryColor();

  // translate the data into table cells for display in the view
  const rows = tableData.get(0).map((monthText, key) => {
    const yearMonth = getYearMonthFromKey(
      key, data.get('startYearMonth')[0], data.get('startYearMonth')[1]);

    const past = yearMonth[0] < currentYear ||
      (yearMonth[0] === currentYear && yearMonth[1] < currentMonth);
    const active = yearMonth[0] === currentYear && yearMonth[1] === currentMonth;
    const future = !past && !active;

    const cells = list(OVERVIEW_COLUMNS).map((column, colKey) => {
      const value = tableData.getIn([colKey, key]);
      let rgb = null;
      if (colKey > 0 && categoryColor[colKey - 1]) {
        rgb = getOverviewScoreColor(
          value, valueRange[colKey - 1], median[colKey - 1], categoryColor[colKey - 1]);
      }
      const editable = column[0] === 'balance';

      return map({
        column,
        value,
        rgb,
        editable
      });
    });

    return map({ cells, past, active, future });
  });

  return rows;
};

/**
 * @function rCalculateOverview
 * @param {Record} reduction: modified reduction
 * @param {integer} pageIndex: page which is modified
 * @param {YMD} newDate: modified item date
 * @param {YMD} oldDate: original item date
 * @param {integer} newItemCost: modified item cost
 * @param {integer} oldItemCost: original item cost
 * @returns {Record} reduction with re-calculated overview data
 */
export const rCalculateOverview = (reduction, pageIndex, newDate, oldDate, newItemCost, oldItemCost) => {
  const overviewKey = PAGES.indexOf('overview');
  const startYearMonth = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'startYearMonth']);

  const newKey = getKeyFromYearMonth(newDate.year, newDate.month, startYearMonth[0], startYearMonth[1]);
  const oldKey = getKeyFromYearMonth(oldDate.year, oldDate.month, startYearMonth[0], startYearMonth[1]);

  const oldCost = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'cost']);
  const numRows = oldCost.get(PAGES[pageIndex]).size;

  // update the changed rows in the overview page
  let newCost = oldCost;
  if (oldKey === newKey) {
    if (oldKey < numRows) {
      newCost = newCost.setIn(
        [PAGES[pageIndex], oldKey],
        oldCost.getIn([PAGES[pageIndex], oldKey]) + newItemCost - oldItemCost
      );
    }
  }
  else {
    if (oldKey < numRows) {
      newCost = newCost.setIn(
        [PAGES[pageIndex], oldKey],
        oldCost.getIn([PAGES[pageIndex], oldKey]) - oldItemCost
      );
    }
    if (newKey < numRows) {
      newCost = newCost.setIn(
        [PAGES[pageIndex], newKey],
        oldCost.getIn([PAGES[pageIndex], newKey]) + newItemCost
      );
    }
  }

  const endYearMonth = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'endYearMonth']);
  const currentYearMonth = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'currentYearMonth']);
  const futureMonths = reduction.getIn(['appState', 'pages', overviewKey, 'data', 'futureMonths']);

  const newData = rProcessDataOverview(
    newCost, startYearMonth, endYearMonth, currentYearMonth, futureMonths);

  return reduction.setIn(['appState', 'pages', overviewKey, 'data'], newData)
  .setIn(['appState', 'pages', overviewKey, 'rows'], rGetOverviewRows(newData));
};

/**
 * Called when data is first loaded
 * @param {Record} reduction: app state
 * @param {integer} pageIndex: page index
 * @param {object} raw: api JSON data
 * @returns {Record} modified reduction
 */
export default (reduction, pageIndex, raw) => {
  const data = rProcessDataOverviewRaw(raw);
  const rows = rGetOverviewRows(data);

  return reduction.setIn(['appState', 'pages', pageIndex], map({ data, rows }));
};

<|endoftext|>


<commit_before>/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var parser = require('./parser')
  , util = require('./util')
  , EventEmitter = require('events').EventEmitter

/**
 * Export the constructor.
 */

exports = module.exports = Socket;

/**
 * Default error event listener to prevent uncaught exceptions.
 */

var defaultError = function () {};

/**
 * Socket constructor.
 *
 * @param {Manager} manager instance
 * @param {String} session id
 * @param {Namespace} namespace the socket belongs to
 * @param {Boolean} whether the
 * @api public
 */

function Socket (manager, id, handshaken) {
  this.readyState = Socket.CONNECTED;
  this.id = id;
  this.manager = manager;
  this.ackPackets = 0;
  this.acks = {};
  this.handshaken = handshaken;
  this.store = this.manager.store;
  this.setFlags();
  this.on('error', defaultError);
};

/**
 * Socket readyState
 */

Socket.CONNECTED = 0;
Socket.DISCONNECTING = 1;
Socket.DISCONNECTED = 2;

/**
 * Inherits from EventEmitter.
 */

Socket.prototype.__proto__ = EventEmitter.prototype;

/**
 * Accessor shortcut for the handshake data
 *
 * @api private
 */

Socket.prototype.__defineGetter__('handshake', function () {
  return this.handshaken;
});

/**
 * Accessor shortcut for the transport type
 *
 * @api private
 */

Socket.prototype.__defineGetter__('transport', function () {
  return this.manager.transportMap[this.id];
});

Socket.prototype.__defineGetter__('json', function() {
  this.flags.json = true;
  return this;
});

Socket.prototype.setFlags = function() {
  this.flags = {};
  return this;
}

/**
 * Accessor shortcut for the logger.
 *
 * @api private
 */

Socket.prototype.__defineGetter__('log', function () {
  return this.manager.log;
});

/**
 * Triggered on disconnect
 *
 * @api private
 */

Socket.prototype.onDisconnect = function (reason) {
  if (this.readyState !== Socket.DISCONNECTED) {
    this.$emit('disconnect', reason);
    this.readyState = Socket.DISCONNECTED;
  }
};

/**
 * Transmits a packet.
 *
 * @api private
 */

Socket.prototype.packet = function (packet) {
  if (this.readyState !== Socket.DISCONNECTED) {
    this.transport.packet(packet);
  }
  return this;
};

/**
 * Kicks client
 *
 * @api public
 */

Socket.prototype.disconnect = function () {
  if (this.readyState !== Socket.DISCONNECTED) {
    this.readyState = Socket.DISCONNECTING;
    this.log.info('booting client');
    this.transport.disconnect('booted');
    this.acks = null;
  }

  return this;
};

/**
 * Send a message.
 *
 * @api public
 */

Socket.prototype.send = function (data, fn) {
  var packet = {
      type: this.flags.json ? 'json' : 'message'
    , data: data
  };

  if (fn) {
    packet.id = ++this.ackPackets;
    packet.ack = true;
    this.acks[packet.id] = fn;
  }

  return this.packet(packet);
};

/**
 * Original emit function.
 *
 * @api private
 */

Socket.prototype.$emit = EventEmitter.prototype.emit;

/**
 * Emit override for custom events.
 *
 * @api public
 */

Socket.prototype.emit = function (ev) {
  if (ev === 'newListener') {
    return this.$emit.apply(this, arguments);
  }

  var args = Array.prototype.slice(arguments, 1)
    , lastArg = args[args.length - 1]
    , packet = {
          type: 'event'
        , name: ev
      };

  if ('function' === typeof lastArg) {
    packet.id = ++this.ackPackets;
    packet.ack = lastArg.length ? 'data' : true;
    this.acks[packet.id] = lastArg;
    args = args.slice(0, args.length - 1);
  }

  packet.args = args;

  return this.packet(packet);
};
<commit_msg>socket: add some comment<commit_after>/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var parser = require('./parser')
  , util = require('./util')
  , EventEmitter = require('events').EventEmitter

/**
 * Export the constructor.
 */

exports = module.exports = Socket;

/**
 * Default error event listener to prevent uncaught exceptions.
 */

var defaultError = function () {};

/**
 * Socket constructor.
 *
 * @param {Manager} manager instance
 * @param {String} session id
 * @param {Namespace} namespace the socket belongs to
 * @param {Boolean} whether the
 * @api public
 */

function Socket (manager, id, handshaken) {
  this.readyState = Socket.CONNECTED;
  this.id = id;
  this.manager = manager;
  this.ackPackets = 0;
  this.acks = {};
  this.handshaken = handshaken;
  this.store = this.manager.store;
  this.setFlags();
  this.on('error', defaultError);
};

/**
 * Socket readyState
 */

Socket.CONNECTED = 0;
Socket.DISCONNECTING = 1;
Socket.DISCONNECTED = 2;

/**
 * Inherits from EventEmitter.
 */

Socket.prototype.__proto__ = EventEmitter.prototype;

/**
 * Accessor shortcut for the handshake data
 * It is just a cache for the data in Store
 *
 * @api private
 */

Socket.prototype.__defineGetter__('handshake', function () {
  return this.handshaken;
});

/**
 * Accessor shortcut for the transport type
 *
 * @api private
 */

Socket.prototype.__defineGetter__('transport', function () {
  return this.manager.transportMap[this.id];
});

/**
 * JSON message flag.
 *
 * @api public
 */

Socket.prototype.__defineGetter__('json', function() {
  this.flags.json = true;
  return this;
});

/**
 * Resets flags
 *
 * @api private
 */

Socket.prototype.setFlags = function() {
  this.flags = {};
  return this;
}

/**
 * Accessor shortcut for the logger.
 *
 * @api private
 */

Socket.prototype.__defineGetter__('log', function () {
  return this.manager.log;
});

/**
 * Triggered on disconnect
 *
 * @api private
 */

Socket.prototype.onDisconnect = function (reason) {
  if (this.readyState !== Socket.DISCONNECTED) {
    this.$emit('disconnect', reason);
    this.readyState = Socket.DISCONNECTED;
  }
};

/**
 * Transmits a packet.
 *
 * @api private
 */

Socket.prototype.packet = function (packet) {
  if (this.readyState !== Socket.DISCONNECTED) {
    this.transport.packet(packet);
  }

  this.setFlags();
  return this;
};

/**
 * Kicks client
 *
 * @api public
 */

Socket.prototype.disconnect = function () {
  if (this.readyState !== Socket.DISCONNECTED) {
    this.readyState = Socket.DISCONNECTING;
    this.log.info('booting client');
    this.transport.disconnect('booted');
    this.acks = null;
  }

  return this;
};

/**
 * Send a message.
 *
 * @api public
 */

Socket.prototype.send = function (data, fn) {
  var packet = {
      type: this.flags.json ? 'json' : 'message'
    , data: data
  };

  if (fn) {
    packet.id = ++this.ackPackets;
    packet.ack = true;
    this.acks[packet.id] = fn;
  }

  return this.packet(packet);
};

/**
 * Original emit function.
 *
 * @api private
 */

Socket.prototype.$emit = EventEmitter.prototype.emit;

/**
 * Emit override for custom events.
 *
 * @api public
 */

Socket.prototype.emit = function (ev) {
  if (ev === 'newListener') {
    return this.$emit.apply(this, arguments);
  }

  var args = Array.prototype.slice(arguments, 1)
    , lastArg = args[args.length - 1]
    , packet = {
          type: 'event'
        , name: ev
      };

  if ('function' === typeof lastArg) {
    packet.id = ++this.ackPackets;
    packet.ack = lastArg.length ? 'data' : true;
    this.acks[packet.id] = lastArg;
    args = args.slice(0, args.length - 1);
  }

  packet.args = args;

  return this.packet(packet);
};
<|endoftext|>


<commit_before>openerp.web_tour_extra = function(instance) {
	var Tour = instance.web.Tour;

	instance.web.Tour.getState = function () {
		var state = JSON.parse(localStorage.getItem("tour") || 'false') || {};
		if (state) {
			this.time = state.time;
		}
		var tour_id, mode, step_id;
		// tutorial_extra instead of tutorial
		if (!state.id && window.location.href.indexOf("#tutorial_extra.") > -1) {
			this.tutorial_extra = true;
			state = {
				"id": window.location.href.match(/#tutorial_extra\.(.*)=true/)[1],
				"mode": "tutorial",
				"step_id": 0
			};
			// Do not clean hash for reasons of saving starting tour path.
			window.location.hash = window.location.hash.replace(RegExp("&?/?#tutorial[^=]*=true"), "");
			Tour.log("Tour '" + state.id + "' Begin from url hash");
			Tour.saveState(state.id, state.mode, state.step_id, 0);
		}
		if (!state.id) {
			return;
		}
		state.tour = Tour.tours[state.id];
		state.step = state.tour && state.tour.steps[state.step_id === -1 ? 0 : state.step_id];
		return state;
	};
	$(document).ready(function () {
    if (Tour.autoRunning) {
        Tour.running();
    };
});

};
<commit_msg>[FIX] do not run tour again on F5<commit_after>openerp.web_tour_extra = function(instance) {
	var Tour = instance.web.Tour;

	instance.web.Tour.getState = function () {
		var state = JSON.parse(localStorage.getItem("tour") || 'false') || {};
		if (state) {
			this.time = state.time;
		}
		var tour_id, mode, step_id;
		// tutorial_extra instead of tutorial
		if (!state.id && window.location.href.indexOf("#tutorial_extra.") > -1) {
			this.tutorial_extra = true;
			state = {
				"id": window.location.href.match(/#tutorial_extra\.(.*)=true/)[1],
				"mode": "tutorial",
				"step_id": 0
			};
			// Do not clean hash for reasons of saving starting tour path.
			window.location.hash = window.location.hash.replace(RegExp("&?/?#tutorial[^=]*=true"), "");
			Tour.log("Tour '" + state.id + "' Begin from url hash");
			Tour.saveState(state.id, state.mode, state.step_id, 0);
		}
		if (!state.id) {
			return;
		}
		state.tour = Tour.tours[state.id];
		state.step = state.tour && state.tour.steps[state.step_id === -1 ? 0 : state.step_id];
		return state;
	};
	$(document).ready(function () {
	if(localStorage.getItem("tour"))
    {return};
    if (Tour.autoRunning) {
        Tour.running();
    };
});

};
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter;
var Bottleneck = require('bottleneck');
var debug = require('debug')('sparky');
var util = require("util");
var req = require('request');
var _ = require('lodash');

function Sparky(config) {
  this.config = config;

  var self = this;

  // enable additional debug() events
  self.DEBUG = false;

  // validate token has been passed
  if(!self.config.token) throw new Error('missing token');

  // enable event emitter
  EventEmitter.call(self);

  // max search results
  self._maxItems = self.config.maxItems || 500;

  // max concurrent requests
  self._maxConcurrent = self.config.maxConcurrent || 2;

  // min time between requests
  self._minTime = self.config.minTime || 500;

  // time to wait between failed responses before retry
  self._requeueMinTime = self.config.requeueMinTime || self._minTime * 10;

  // max number of attempt to make for failed response
  self._requeueMaxRetry = self.config.requeueMaxRetry || 3;

  // response codes to requeue
  self._requeueCodes = self.config.requeueCodes || [ 429, 500, 503 ];

  // time to wait to recieve a response from api before failing
  self._requestTimeout = self.config.requestTimeout || 5000;

  // calculate queue sizes for limiters
  self._queueDepthTime = self.config.queueDepthTime || 10000;
  self._requeueDepthTime = self.config.requeueDepthTime || Math.floor(self._queueDepthTime * self._requeueMinTime / self._minTime);
  self._queueSize = Math.floor(self._queueDepthTime / self._minTime);
  self._requeueSize = Math.floor(self._requeueDepthTime / self._requeueMinTime);

  // api url
  self._url = self.config.url || 'https://api.ciscospark.com/v1/';

  // webhook
  self._webhook = self.config.webhook || null;

  // token
  self.token = self.config.token;

  // queue for API calls
  self.queue = new Bottleneck(self._maxConcurrent, self._minTime, self._queueSize);

  // queue for API calls that fail
  self.requeue = new Bottleneck(self._maxConcurrent, self._requeueMinTime, self._requeueSize);

  // queue for API calls that overflow
  self.overflow = new Bottleneck(self._maxConcurrent, Math.floor(self._minTime / 2), self._queueSize * 2);

  // handle internal events
  self.on('request', function(url, requestOptions) {
    if(self.DEBUG) {
      debug('requested: %s', url || '<empty>');
    }
  });
  self.on('response', function(response) {
    if(self.DEBUG && response && response.headers && response.headers.trackingid) {
      debug('received response %s in %s(ms) with tracking id: %s', response.statusCode, response.elapsedTime, response.headers.trackingid);
    }
    else if( response && !response.ok && response.headers && response.headers.trackingid) {
      debug('received response %s in %s(ms) with tracking id: %s', response.statusCode, response.elapsedTime, response.headers.trackingid);
    }
  });
  self.on('retry', function(response) {
    if(self.DEBUG && response && response.headers) {
      debug('retry: %s', response.headers.trackingid);
    }
  });
  self.on('error', function(err) {
    if(err) {
      debug('%s', err);
    }
  });

  // requeue to overflow
  self.queue.on('dropped', function(request) {
    if(self.DEBUG) {
      debug('queue size exceeded, dropping oldest request');
    }
    self.queueRequest(self.overflow, request);
  });

  // emit drop events from queue
  self.overflow.on('dropped', function(request) {
    debug('queue size exceeded, dropping oldest request');
    self.emit('dropped', request);

  });

  // emit drop event from requeue
  self.requeue.on('dropped', function(request) {
    debug('requeue size exceeded, dropping oldest request');
    self.emit('dropped', request);
  });

  // load sparky api commands
  _.merge(self, self._run());
}
util.inherits(Sparky, EventEmitter);

// queue request
Sparky.prototype.queueRequest = function(queue, request) {
  queue.submit(request, null);
}

// error handler
Sparky.prototype.errorHandler = function(message) {
  var self = this;

  // pargs arguments
  var args = Array.prototype.slice.call(arguments);

  // apply formatters to message and generate error
  var error = new Error(util.format.apply(this, args));

  // emit error
  self.emit('error', error);

  // return error object
  return error;
}

// perform api call
Sparky.prototype.call = function(method, resource, id, query, callback) {
  var self = this;

  // define headers
  var headers = {
    'Authorization': 'Bearer ' + self.token,
    'Content-Type': 'application/json'
  };

  // init error counter
  var errorCount = 0;

  // parse args
  var args = Array.prototype.slice.call(arguments);
  method = args.shift();
  resource = args.shift();
  // optional args
  id = (args.length > 0 && typeof args[0] === 'string') ? args.shift() : null;
  query = (args.length > 0 && typeof args[0] === 'object') ? args.shift() : null;
  callback = (args.length > 0 && typeof args[0] === 'function') ? args.shift() : null;

  // generate url
  id = id ? '/' + id : '';
  var url = self._url + resource + id;

  // define request options
  var requestOptions = {
    url: url,
    headers: headers,
    method: method,
    timeout: self._requestTimeout,
    gzip: true,
    time: true
  };
  if(resource === 'contents') {
    requestOptions.encoding = 'binary';
  } else {
    requestOptions.json = true;
  }

  // define request content
  if(query) {
    if(method === 'post' || method === 'put') {
      requestOptions.body = query;
    } else {
      requestOptions.qs = query;
    }
  }

  // send request
  function request(end){
    req(requestOptions, requestCallback).on('end', end);
  }

  // callback from request
  function requestCallback(err, response, body) {
    if(err) {
      // emit error on processing request
      err = self.errorHandler('processing spark api response');

      callback ? callback(err, null) : null;
    } else {
      response.body = body || response.body;
      response.ok = (response.statusCode >= 200 && response.statusCode < 300);

      // emit response event
      self.emit('response', response);

      // catch specific error response codes to requeue
      if(_.includes(self._requeueCodes, response.statusCode)) {
        // retry
        requestRetry(response);
      }

      // if 204 received from successfull delete
      else if(response.statusCode === 204) {
        callback ? callback(null, null) : null;
      }

      // catch file response from contents api
      else if(response.ok && response.headers && response.body
        && !response.headers['content-type'].includes('application/json')
        && resource === 'contents')
      {
        callback ? callback(null, response) : null;
      }

      // if normal response
      else if(response.ok && response.body) {
        // if items array found
        if(response.body.items && response.body.items.length > 0) {
          callback ? callback(null, response.body.items) : null;
        }

        // if items array not found and single object located in body
        else if(!response.body.items && response.body.id) {
          // return as array
          callback ? callback(null, [ response.body ]) : null;
        }

        // no item(s) returned
        else {
          callback ? callback(null, []) : null;
        }
      } else {
        // emit error for processing response
        err = self.errorHandler('processing spark api response');

        callback ? callback(err, null) : null;
      }
    }
  }

  // retry request
  function requestRetry(response) {
    // increment error counter
    errorCount++;

    // if error count exceeded
    if(errorCount > self._requeueMaxRetry) {
      var err = self.errorHandler('failed after %s retries', self._requeueMaxRetry);

      callback ? callback(err, null) : null;
    } else {
      // emit error for retry count
      self.errorHandler('retry #%s for request that generated %s response', errorCount, response.statusCode || 0);

      setTimeout(function() {

        // emit retry event
        self.emit('retry', response);

        // requeue request
        self.requeue.submit(request, null);

      }, self._requeueMinTime);
    }
  }

  // emit request event
  self.emit('request', url, requestOptions);

  // submit initial request
  self.queueRequest(self.queue, request);
};

// api commands
Sparky.prototype._run = function() {
  var self = this;

  return {
    rooms: {
      get: function(cb) {
        self.call('get', 'rooms', { max: self._maxItems }, cb);
      }
    },
    room: {
      get: function(id, cb) {
        self.call('get', 'rooms', id, { showSipAddress: true }, cb);
      },
      add: function(title, cb) {
        self.call('post', 'rooms', { title: title }, cb);
      },
      rename: function(id, title, cb) {
        self.call('put', 'rooms', id, { title: title }, cb);
      },
      remove: function(id, cb) {
        self.call('delete', 'rooms', id, cb);
      }
    },
    people: {
      search: function(displayName, cb) {
        self.call('get', 'people', {
          displayName: displayName,
          max: self._maxItems
        }, cb);
      }
    },
    person: {
      get: function(id, cb) {
        self.call('get', 'people', id, cb);
      },
      me: function(cb) {
        self.call('get', 'people', 'me', cb);
      },
      byEmail: function(email, cb) {
        self.call('get', 'people', { email: email }, cb);
      }
    },
    messages: {
      get: function(roomId, max, cb) {
        // parse args
        var args = Array.prototype.slice.call(arguments);
        roomId = args.shift();
        cb = args.pop();
        // optional args
        max = (args.length > 0 && typeof args[0] === 'number') ? args.shift() : null;

        self.call('get', 'messages', {
          roomId: roomId,
          max: max || self._maxItems
        }, cb);
      }
    },
    message: {
      get: function(id, cb) {
        self.call('get', 'messages', id, cb);
      },
      send: {
        person: function(email, message, cb) {
          message.toPersonEmail = email;
          self.call('post', 'messages', message, cb);
        },
        room: function(roomId, message, cb) {
          message.roomId = roomId;
          self.call('post', 'messages', message, cb);
        }
      },
      remove: function(id, cb) {
        self.call('delete', 'messages', id, cb);
      }
    },
    contents: {
      get: function(id, cb) {
        self.call('get', 'contents', id, function(err, res) {
          if(err) {
            cb(err, null);
          } else {
            // get file
            var contents = {};
            contents.name = res.headers['content-disposition'].match(/"(.*)"/)[1];
            contents.type = res.headers['content-type'];
            contents.binary = new Buffer(res.body, 'binary');
            contents.base64 = new Buffer(res.body, 'binary').toString('base64');

            cb(null, contents);
          }
        });
      },
      byUrl: function(url, cb) {
        var id = url.match(/contents\/(.*)/)[1];
        self.contents.get(id, cb);
      }
    },
    memberships: {
      get: function(cb) {
        self.call('get', 'memberships', { max: self._maxItems }, cb);
      },
      byRoom: function(roomId, cb) {
        self.call('get', 'memberships', {
          roomId: roomId,
          max: self._maxItems
        }, cb);
      }
    },
    membership: {
      get: function(id, cb) {
        self.call('get', 'memberships', id, cb);
      },
      byRoomByEmail: function(roomId, personEmail, cb) {
        self.call('get', 'memberships', {
          roomId: roomId,
          personEmail: personEmail
        }, cb);
      },
      add: function(roomId, email, cb) {
        self.call('post', 'memberships', {
          personEmail: email,
          roomId: roomId,
          isModerator: false
        }, cb);
      },
      set: {
        moderator: function(id, cb) {
            self.call('put', 'memberships', id, { isModerator: false }, cb);
        }
      },
      clear: {
        moderator: function(id, cb) {
            self.call('put', 'memberships', id, { isModerator: false }, cb);
        }
      },
      remove: function(id, cb) {
        self.call('delete', 'memberships', id, cb);
      }
    },
    webhooks: {
      get: function(cb) {
        self.call('get', 'webhooks', { max: self._maxItems }, cb);
      }
    },
    webhook: {
      get: function(id, cb) {
        self.call('get', 'webhooks', id, cb);
      },
      add: {
        messages: {
          created: {
            room: function(roomId, name, cb) {
              // parse args
              var args = Array.prototype.slice.call(arguments);
              roomId = args.shift();
              cb = args.pop();
              // optional args
              name = (args.length > 0 && typeof args[0] === 'string') ? args.shift() : null;

              // check if webhook url is defined in options
              if(!self._webhook) throw new Error('webhook url not specified');
              self.call('post', 'webhooks', {
                resource: 'messages',
                event: 'created',
                filter: 'roomId=' + roomId,
                targetUrl: self._webhook,
                name: name || roomId
              }, cb);
            }
          }
        }
      },
      remove: function(id, cb) {
        self.call('delete', 'webhooks', id, cb);
      }
    }
  };

};

module.exports = Sparky;<commit_msg>updated<commit_after>var EventEmitter = require('events').EventEmitter;
var Bottleneck = require('bottleneck');
var debug = require('debug')('sparky');
var util = require("util");
var req = require('request');
var _ = require('lodash');

function Sparky(config) {
  this.config = config;

  var self = this;

  // enable additional debug() events
  self.DEBUG = false;

  // validate token has been passed
  if(!self.config.token) throw new Error('missing token');

  // enable event emitter
  EventEmitter.call(self);

  // max search results
  self._maxItems = self.config.maxItems || 500;

  // max concurrent requests
  self._maxConcurrent = self.config.maxConcurrent || 2;

  // min time between requests
  self._minTime = self.config.minTime || 500;

  // time to wait between failed responses before retry
  self._requeueMinTime = self.config.requeueMinTime || self._minTime * 10;

  // max number of attempt to make for failed response
  self._requeueMaxRetry = self.config.requeueMaxRetry || 3;

  // response codes to requeue
  self._requeueCodes = self.config.requeueCodes || [ 429, 500, 503 ];

  // time to wait to recieve a response from api before failing
  self._requestTimeout = self.config.requestTimeout || 5000;

  // calculate queue sizes for limiters
  self._queueDepthTime = self.config.queueDepthTime || 10000;
  self._requeueDepthTime = self.config.requeueDepthTime || Math.floor(self._queueDepthTime * self._requeueMinTime / self._minTime);
  self._queueSize = Math.floor(self._queueDepthTime / self._minTime);
  self._requeueSize = Math.floor(self._requeueDepthTime / self._requeueMinTime);

  // api url
  self._url = self.config.url || 'https://api.ciscospark.com/v1/';

  // webhook
  self._webhook = self.config.webhook || null;

  // token
  self.token = self.config.token;

  // queue for API calls
  self.queue = new Bottleneck(self._maxConcurrent, self._minTime, self._queueSize);

  // queue for API calls that fail
  self.requeue = new Bottleneck(self._maxConcurrent, self._requeueMinTime, self._requeueSize);

  // handle internal events
  self.on('request', function(url, requestOptions) {
    if(self.DEBUG) {
      debug('requested: %s', url || '<empty>');
    }
  });
  self.on('response', function(response) {
    if(self.DEBUG && response && response.headers && response.headers.trackingid) {
      debug('received response %s in %s(ms) with tracking id: %s', response.statusCode, response.elapsedTime, response.headers.trackingid);
    }
    else if( response && !response.ok && response.headers && response.headers.trackingid) {
      debug('received response %s in %s(ms) with tracking id: %s', response.statusCode, response.elapsedTime, response.headers.trackingid);
    }
  });
  self.on('retry', function(response) {
    if(self.DEBUG && response && response.headers) {
      debug('retry: %s', response.headers.trackingid);
    }
  });
  self.on('error', function(err) {
    if(err) {
      debug('%s', err);
    }
  });

  // emit drop events from queue
  self.queue.on('dropped', function(request) {
    debug('queue size exceeded, dropping oldest request');
    self.emit('dropped', request);
  });

  // emit drop event from requeue
  self.requeue.on('dropped', function(request) {
    debug('requeue size exceeded, dropping oldest request');
    self.emit('dropped', request);
  });

  // load sparky api commands
  _.merge(self, self._run());
}
util.inherits(Sparky, EventEmitter);

// queue request
Sparky.prototype.queueRequest = function(queue, request) {
  queue.submit(request, null);
}

// error handler
Sparky.prototype.errorHandler = function(message) {
  var self = this;

  // pargs arguments
  var args = Array.prototype.slice.call(arguments);

  // apply formatters to message and generate error
  var error = new Error(util.format.apply(this, args));

  // emit error
  self.emit('error', error);

  // return error object
  return error;
}

// perform api call
Sparky.prototype.call = function(method, resource, id, query, callback) {
  var self = this;

  // define headers
  var headers = {
    'Authorization': 'Bearer ' + self.token,
    'Content-Type': 'application/json'
  };

  // init error counter
  var errorCount = 0;

  // parse args
  var args = Array.prototype.slice.call(arguments);
  method = args.shift();
  resource = args.shift();
  // optional args
  id = (args.length > 0 && typeof args[0] === 'string') ? args.shift() : null;
  query = (args.length > 0 && typeof args[0] === 'object') ? args.shift() : null;
  callback = (args.length > 0 && typeof args[0] === 'function') ? args.shift() : null;

  // generate url
  id = id ? '/' + id : '';
  var url = self._url + resource + id;

  // define request options
  var requestOptions = {
    url: url,
    headers: headers,
    method: method,
    timeout: self._requestTimeout,
    gzip: true,
    time: true
  };
  if(resource === 'contents') {
    requestOptions.encoding = 'binary';
  } else {
    requestOptions.json = true;
  }

  // define request content
  if(query) {
    if(method === 'post' || method === 'put') {
      requestOptions.body = query;
    } else {
      requestOptions.qs = query;
    }
  }

  // send request
  function request(end){
    req(requestOptions, requestCallback).on('end', end);
  }

  // callback from request
  function requestCallback(err, response, body) {
    if(err) {
      // emit error on processing request
      err = self.errorHandler('processing spark api response');

      callback ? callback(err, null) : null;
    } else {
      response.body = body || response.body;
      response.ok = (response.statusCode >= 200 && response.statusCode < 300);

      // emit response event
      self.emit('response', response);

      // catch specific error response codes to requeue
      if(_.includes(self._requeueCodes, response.statusCode)) {
        // retry
        requestRetry(response);
      }

      // if 204 received from successfull delete
      else if(response.statusCode === 204) {
        callback ? callback(null, null) : null;
      }

      // catch file response from contents api
      else if(response.ok && response.headers && response.body
        && !response.headers['content-type'].includes('application/json')
        && resource === 'contents')
      {
        callback ? callback(null, response) : null;
      }

      // if normal response
      else if(response.ok && response.body) {
        // if items array found
        if(response.body.items && response.body.items.length > 0) {
          callback ? callback(null, response.body.items) : null;
        }

        // if items array not found and single object located in body
        else if(!response.body.items && response.body.id) {
          // return as array
          callback ? callback(null, [ response.body ]) : null;
        }

        // no item(s) returned
        else {
          callback ? callback(null, []) : null;
        }
      } else {
        // emit error for processing response
        err = self.errorHandler('processing spark api response');

        callback ? callback(err, null) : null;
      }
    }
  }

  // retry request
  function requestRetry(response) {
    // increment error counter
    errorCount++;

    // if error count exceeded
    if(errorCount > self._requeueMaxRetry) {
      var err = self.errorHandler('failed after %s retries', self._requeueMaxRetry);

      callback ? callback(err, null) : null;
    } else {
      // emit error for retry count
      self.errorHandler('retry #%s for request that generated %s response', errorCount, response.statusCode || 0);

      setTimeout(function() {

        // emit retry event
        self.emit('retry', response);

        // requeue request
        self.requeue.submit(request, null);

      }, self._requeueMinTime);
    }
  }

  // emit request event
  self.emit('request', url, requestOptions);

  // submit initial request
  self.queueRequest(self.queue, request);
};

// api commands
Sparky.prototype._run = function() {
  var self = this;

  return {
    rooms: {
      get: function(cb) {
        self.call('get', 'rooms', { max: self._maxItems }, cb);
      }
    },
    room: {
      get: function(id, cb) {
        self.call('get', 'rooms', id, { showSipAddress: true }, cb);
      },
      add: function(title, cb) {
        self.call('post', 'rooms', { title: title }, cb);
      },
      rename: function(id, title, cb) {
        self.call('put', 'rooms', id, { title: title }, cb);
      },
      remove: function(id, cb) {
        self.call('delete', 'rooms', id, cb);
      }
    },
    people: {
      search: function(displayName, cb) {
        self.call('get', 'people', {
          displayName: displayName,
          max: self._maxItems
        }, cb);
      }
    },
    person: {
      get: function(id, cb) {
        self.call('get', 'people', id, cb);
      },
      me: function(cb) {
        self.call('get', 'people', 'me', cb);
      },
      byEmail: function(email, cb) {
        self.call('get', 'people', { email: email }, cb);
      }
    },
    messages: {
      get: function(roomId, max, cb) {
        // parse args
        var args = Array.prototype.slice.call(arguments);
        roomId = args.shift();
        cb = args.pop();
        // optional args
        max = (args.length > 0 && typeof args[0] === 'number') ? args.shift() : null;

        self.call('get', 'messages', {
          roomId: roomId,
          max: max || self._maxItems
        }, cb);
      }
    },
    message: {
      get: function(id, cb) {
        self.call('get', 'messages', id, cb);
      },
      send: {
        person: function(email, message, cb) {
          message.toPersonEmail = email;
          self.call('post', 'messages', message, cb);
        },
        room: function(roomId, message, cb) {
          message.roomId = roomId;
          self.call('post', 'messages', message, cb);
        }
      },
      remove: function(id, cb) {
        self.call('delete', 'messages', id, cb);
      }
    },
    contents: {
      get: function(id, cb) {
        self.call('get', 'contents', id, function(err, res) {
          if(err) {
            cb(err, null);
          } else {
            // get file
            var contents = {};
            contents.name = res.headers['content-disposition'].match(/"(.*)"/)[1];
            contents.type = res.headers['content-type'];
            contents.binary = new Buffer(res.body, 'binary');
            contents.base64 = new Buffer(res.body, 'binary').toString('base64');

            cb(null, contents);
          }
        });
      },
      byUrl: function(url, cb) {
        var id = url.match(/contents\/(.*)/)[1];
        self.contents.get(id, cb);
      }
    },
    memberships: {
      get: function(cb) {
        self.call('get', 'memberships', { max: self._maxItems }, cb);
      },
      byRoom: function(roomId, cb) {
        self.call('get', 'memberships', {
          roomId: roomId,
          max: self._maxItems
        }, cb);
      }
    },
    membership: {
      get: function(id, cb) {
        self.call('get', 'memberships', id, cb);
      },
      byRoomByEmail: function(roomId, personEmail, cb) {
        self.call('get', 'memberships', {
          roomId: roomId,
          personEmail: personEmail
        }, cb);
      },
      add: function(roomId, email, cb) {
        self.call('post', 'memberships', {
          personEmail: email,
          roomId: roomId,
          isModerator: false
        }, cb);
      },
      set: {
        moderator: function(id, cb) {
            self.call('put', 'memberships', id, { isModerator: false }, cb);
        }
      },
      clear: {
        moderator: function(id, cb) {
            self.call('put', 'memberships', id, { isModerator: false }, cb);
        }
      },
      remove: function(id, cb) {
        self.call('delete', 'memberships', id, cb);
      }
    },
    webhooks: {
      get: function(cb) {
        self.call('get', 'webhooks', { max: self._maxItems }, cb);
      }
    },
    webhook: {
      get: function(id, cb) {
        self.call('get', 'webhooks', id, cb);
      },
      add: {
        messages: {
          created: {
            room: function(roomId, name, cb) {
              // parse args
              var args = Array.prototype.slice.call(arguments);
              roomId = args.shift();
              cb = args.pop();
              // optional args
              name = (args.length > 0 && typeof args[0] === 'string') ? args.shift() : null;

              // check if webhook url is defined in options
              if(!self._webhook) throw new Error('webhook url not specified');
              self.call('post', 'webhooks', {
                resource: 'messages',
                event: 'created',
                filter: 'roomId=' + roomId,
                targetUrl: self._webhook,
                name: name || roomId
              }, cb);
            }
          }
        }
      },
      remove: function(id, cb) {
        self.call('delete', 'webhooks', id, cb);
      }
    }
  };

};

module.exports = Sparky;<|endoftext|>


<commit_before>'use strict';

var Reflux = require('reflux');
var api = require('data/api');

var PermissionStore = Reflux.createStore({
  init() {
    this.permissions = [];

    this.permissionsPromise = api.user_permissions()
      .then(data => {
        this.permissions = data.objects.map(p => {
          return p.auth_code
        });
        this.trigger({
          permissions: this.permissions
        });
        return this.permissions;
      });
  },

  getInitialState() {
    return {
      permissions: this.permissions
    };
  },

  // API
  userHasPermission(permissionString) {
    console.log("userHasPermission:", permissionString, this.permissions.indexOf(permissionString.toLowerCase()) > -1);
    return this.permissions.indexOf(permissionString.toLowerCase()) > -1;
  },

  getPermissionsPromise() {
    return this.permissionsPromise;
  }
});

module.exports = PermissionStore;
<commit_msg>remove console.log from permission store<commit_after>'use strict';

var Reflux = require('reflux');
var api = require('data/api');

var PermissionStore = Reflux.createStore({
  init() {
    this.permissions = [];

    this.permissionsPromise = api.user_permissions()
      .then(data => {
        this.permissions = data.objects.map(p => {
          return p.auth_code
        });
        this.trigger({
          permissions: this.permissions
        });
        return this.permissions;
      });
  },

  getInitialState() {
    return {
      permissions: this.permissions
    };
  },

  // API
  userHasPermission(permissionString) {
    // console.log("userHasPermission:", permissionString, this.permissions.indexOf(permissionString.toLowerCase()) > -1);
    return this.permissions.indexOf(permissionString.toLowerCase()) > -1;
  },

  getPermissionsPromise() {
    return this.permissionsPromise;
  }
});

module.exports = PermissionStore;
<|endoftext|>


<commit_before>import dom from 'vd'

export default function splash ({ path, name, org, coc, logo, active, total, channels, large, iframe, gcaptcha_sitekey }){
  let div = dom('.splash',
    !iframe && dom('.logos',
      logo && dom('.logo.org'),
      dom('.logo.slack')
    ),
    dom('p',
      'Join ', dom('b', 'Virtual Spaces'),
      ' on Slack.'
    ),
    dom('p.status',
      active
        ? [
          dom('b.active', active), ' users online now of ',
          dom('b.total', total), ' registered.'
        ]
        : [dom('b.total', total), ' users are registered so far.']
    ),
    dom('form id=invite',
      channels && (
        channels.length > 1
          // channel selection when there are multiple
          ? dom('select.form-item name=channel',
              channels.map(channel => {
                return dom('option', { value: channel, text: channel })
              })
            )
          // otherwise a fixed channel
          : dom('input type=hidden name=channel', { value: channels[0] })
      ),
      dom('input.form-item type=email name=email placeholder=you@yourdomain.com '
        + (!iframe ? 'autofocus' : '')),
      dom('br'),
      dom(`div class="g-recaptcha" data-sitekey="${gcaptcha_sitekey}"`),
      coc && dom('.coc',
        dom('label',
          dom('input type=checkbox name=coc value=1'),
          'I agree to the ',
          dom('a', { href: coc, target: '_blank' }, 'Code of Conduct'),
          '.'
        )
      ),
      dom('button.loading', 'Get my Invite')
    ),
    dom('p.signin',
      'or ',
      dom(`a href=https://${org}.slack.com target=_top`, 'sign in'),
      '.'
    ),
    !iframe && dom('footer',
      'powered by ',
      dom('a href=http://rauchg.com/slackin target=_blank', 'slackin')
    ),
    style({ logo, active, large, iframe }),
    // xxx: single build
    dom('script', `
      data = {};
      data.path = ${JSON.stringify(path)};
    `),
    dom('script src=https://cdn.socket.io/socket.io-1.4.4.js'),
    dom(`script src=${path}assets/superagent.js`),
    dom(`script src=${path}assets/client.js`)
  )
  return div
}

const red = '#EE5050'

function style ({ logo, active, large, iframe } = {}){
  var css = dom.style()
  css.add('html', {
    'font-size': large ? '14px' : '10px',
      'background-color':'#202020'
  })

  css.add('.splash', {
    'width': iframe ? '25rem' : '30rem',
    'margin': iframe ? '0' : '20rem auto',
    'text-align': 'center',
    'font-family': '"Helvetica Neue", Helvetica, Arial',
      'color':'#FAFAFA'
  })

  if (iframe) {
    css.add('body, html', {
      'margin': '0',
      'padding': '0',
      'background-color': '#FAFAFA',
      'overflow': 'hidden' // ff
    })

    css.add('.splash', {
      'box-sizing': 'border-box',
      'padding': '1rem'
    })
  }

  if (!iframe) {
    css
    .media('(max-width: 50rem)')
    .add('.splash', {
      'margin-top': '10rem'
    })
  }

  css.add('.head', {
    'margin-bottom': '4rem'
  })

  css.add('.logos', {
    'position': 'relative',
    'margin-bottom': '4rem'
  })

  if (!iframe) {
    css.add('.logo', {
      'width': '4.8rem',
      'height': '4.8rem',
      'display': 'inline-block',
      'background-size': 'cover'
    })

    css.add('.logo.slack', {
      'background-image': 'url(assets/slack.svg)'
    })

    if (logo) {
      let pw = 1 // '+' width in rem
      let lp = 3 // logos separation in rem

      css.add('.logo.org::after', {
        'position': 'absolute',
        'display': 'block',
        'content': '"+"',
        'top': '1.5rem',
        'left': '0',
        'width': '30rem',
        'text-align': 'center',
        'color': '#D6D6D6',
        'font-size': '1.5rem', // can't use rem in font shorthand IE9-10
              // http://codersblock.com/blog/font-shorthand-bug-in-ie10/
        'font-family': 'Helvetica Neue'
      })

      css.add('.logo.org', {
        'background-image': `url(${logo})`,
        'margin-right': `${lp + pw + lp}rem`
      })
    }
  }

  css.add('.coc', {
    'font-size': '1.2rem',
    padding: '1.5rem 0 .5rem',
    color: '#666'
  })

  if (iframe) {
    css.add('.coc', {
      'font-size': '1.1rem',
      'padding-top': '1rem'
    })

    css.add('.coc input', {
      position: 'relative',
      top: '-.2rem'
    })
  }

  css.add('.coc label', {
    cursor: 'pointer'
  })

  css.add('.coc input', {
    'appearance': 'none',
    '-webkit-appearance': 'none',
    border: 'none',
    'vertical-align': 'middle',
    margin: '0 .5rem 0 0'
  })

  css.add('.coc input::after', {
    content: '""',
    display: 'inline-block',
    width: '1.5rem',
    height: '1.5rem',
    'vertical-align': 'middle',
    background: 'url(/assets/checkbox.svg)',
    cursor: 'pointer'
  })

  css.add('.coc input:checked::after', {
    'background-position': 'right'
  })

  css.add('.coc a', {
    color: '#666'
  })

  css.add('.coc a:hover', {
    'background-color': '#666',
    'text-decoration': 'none',
    color: '#fff'
  })

  css.add('p', {
    'font-size': iframe ? '1.2rem' : '1.5rem',
    'margin': iframe ? '0 0 .5rem' : '.5rem 0'
  })

  if (iframe) {
    css.add('p.status', {
      'font-size': '1.1rem'
    })
  }

  css.add('select', {
    'background': 'none'
  })

  css.add('button, .form-item', {
    'font-size': '1.2rem',
    'margin-top': iframe ? '.5rem' : '1rem',
    'vertical-align': 'middle',
    'display': 'block',
    'text-align': 'center',
    'box-sizing': 'border-box',
    'width': '100%',
    'padding': '.9rem'
  })

  css.add('button', {
    'color': '#fff',
    'font-weight': 'bold',
    'border-width': 0,
    'background': red,
    'text-transform': 'uppercase',
    'cursor': 'pointer',
    'appearence': 'none',
    '-webkit-appearence': 'none',
    'outline': '0',
    'transition': 'background-color 150ms ease-in, color 150ms ease-in'
  })

  css.add('button.loading', {
    'pointer-events': 'none'
  })

  css.add('button:disabled', {
    'color': '#9B9B9B',
    'background-color': '#D6D6D6',
    'cursor': 'default',
    'pointer-events': 'none'
  })

  css.add('button.error', {
    'background-color': '#F4001E',
    'text-transform': 'none'
  })

  css.add('button.success:disabled', {
    'color': '#fff',
    'background-color': '#68C200'
  })

  css.add('button:not(.disabled):active', {
    'background-color': '#7A002F'
  })

  css.add('b', {
    'transition': 'transform 150ms ease-in'
  })

  css.add('b.grow', {
    'transform': 'scale(1.3)'
  })

  css.add('form', {
    'margin-top': iframe ? '1rem' : '2rem',
    'margin-bottom': '0'
  })

  css.add('input', {
    'color': '#9B9B9B',
    'border': '.1rem solid #D6D6D6'
  })

  if (iframe) {
    css.add('button, .form-item', {
      'height': '2.8rem',
      'line-height': '2.8rem',
      'padding': 0,
      'font-size': '1.1rem'
    })
  }

  css.add('input:focus', {
    'color': '#666',
    'border-color': '#999',
    'outline': '0'
  })

  if (active) {
    css.add('.active', {
      'color': red
    })
  }

  css.add('p.signin', {
    'padding': '1rem 0 1rem',
    'font-size': '1.1rem'
  })

  css.add('p.signin a', {
    'color': red,
    'text-decoration': 'none'
  })

  css.add('p.signin a:hover', {
    'background-color': '#EE5050',
    color: '#fff'
  })

  if (!iframe) {
    css.add('footer', {
      'color': '#D6D6D6',
      'font-size': '1.1rem',
      'margin': '20rem auto 0',
      'width': '30rem',
      'text-align': 'center'
    })

    css.add('footer a', {
      'color': '#9B9B9B',
      'text-decoration': 'none',
      'border-bottom': '.1rem solid #9B9B9B'
    })

    css.add('footer a:hover', {
      'color': '#fff',
      'background-color': '#9B9B9B'
    })
  }

  return css
}
<commit_msg>font change<commit_after>import dom from 'vd'

export default function splash ({ path, name, org, coc, logo, active, total, channels, large, iframe, gcaptcha_sitekey }){
  let div = dom('.splash',
    !iframe && dom('.logos',
      logo && dom('.logo.org'),
      dom('.logo.slack')
    ),
    dom('p',
      'Join ', dom('b', 'Virtual Spaces'),
      ' on Slack.'
    ),
    dom('p.status',
      active
        ? [
          dom('b.active', active), ' users online now of ',
          dom('b.total', total), ' registered.'
        ]
        : [dom('b.total', total), ' users are registered so far.']
    ),
    dom('form id=invite',
      channels && (
        channels.length > 1
          // channel selection when there are multiple
          ? dom('select.form-item name=channel',
              channels.map(channel => {
                return dom('option', { value: channel, text: channel })
              })
            )
          // otherwise a fixed channel
          : dom('input type=hidden name=channel', { value: channels[0] })
      ),
      dom('input.form-item type=email name=email placeholder=you@yourdomain.com '
        + (!iframe ? 'autofocus' : '')),
      dom('br'),
      dom(`div class="g-recaptcha" data-sitekey="${gcaptcha_sitekey}"`),
      coc && dom('.coc',
        dom('label',
          dom('input type=checkbox name=coc value=1'),
          'I agree to the ',
          dom('a', { href: coc, target: '_blank' }, 'Code of Conduct'),
          '.'
        )
      ),
      dom('button.loading', 'Get my Invite')
    ),
    dom('p.signin',
      'or ',
      dom(`a href=https://${org}.slack.com target=_top`, 'sign in'),
      '.'
    ),
    !iframe && dom('footer',
      'powered by ',
      dom('a href=http://rauchg.com/slackin target=_blank', 'slackin')
    ),
    style({ logo, active, large, iframe }),
    // xxx: single build
    dom('script', `
      data = {};
      data.path = ${JSON.stringify(path)};
    `),
    dom('script src=https://cdn.socket.io/socket.io-1.4.4.js'),
    dom(`script src=${path}assets/superagent.js`),
    dom(`script src=${path}assets/client.js`)
  )
  return div
}

const red = '#EE5050'

function style ({ logo, active, large, iframe } = {}){
  var css = dom.style()
  css.add('html', {
    'font-size': large ? '14px' : '10px',
      'background-color':'#202020'
  })

  css.add('.splash', {
    'width': iframe ? '25rem' : '30rem',
    'margin': iframe ? '0' : '20rem auto',
    'text-align': 'center',
    'font-family': '"sofia-pro", Helvetica, Arial',
      'color':'#FAFAFA'
  })

  if (iframe) {
    css.add('body, html', {
      'margin': '0',
      'padding': '0',
      'background-color': '#FAFAFA',
      'overflow': 'hidden' // ff
    })

    css.add('.splash', {
      'box-sizing': 'border-box',
      'padding': '1rem'
    })
  }

  if (!iframe) {
    css
    .media('(max-width: 50rem)')
    .add('.splash', {
      'margin-top': '10rem'
    })
  }

  css.add('.head', {
    'margin-bottom': '4rem'
  })

  css.add('.logos', {
    'position': 'relative',
    'margin-bottom': '4rem'
  })

  if (!iframe) {
    css.add('.logo', {
      'width': '4.8rem',
      'height': '4.8rem',
      'display': 'inline-block',
      'background-size': 'cover'
    })

    css.add('.logo.slack', {
      'background-image': 'url(assets/slack.svg)'
    })

    if (logo) {
      let pw = 1 // '+' width in rem
      let lp = 3 // logos separation in rem

      css.add('.logo.org::after', {
        'position': 'absolute',
        'display': 'block',
        'content': '"+"',
        'top': '1.5rem',
        'left': '0',
        'width': '30rem',
        'text-align': 'center',
        'color': '#D6D6D6',
        'font-size': '1.5rem', // can't use rem in font shorthand IE9-10
              // http://codersblock.com/blog/font-shorthand-bug-in-ie10/
        'font-family': 'Helvetica Neue'
      })

      css.add('.logo.org', {
        'background-image': `url(${logo})`,
        'margin-right': `${lp + pw + lp}rem`
      })
    }
  }

  css.add('.coc', {
    'font-size': '1.2rem',
    padding: '1.5rem 0 .5rem',
    color: '#666'
  })

  if (iframe) {
    css.add('.coc', {
      'font-size': '1.1rem',
      'padding-top': '1rem'
    })

    css.add('.coc input', {
      position: 'relative',
      top: '-.2rem'
    })
  }

  css.add('.coc label', {
    cursor: 'pointer'
  })

  css.add('.coc input', {
    'appearance': 'none',
    '-webkit-appearance': 'none',
    border: 'none',
    'vertical-align': 'middle',
    margin: '0 .5rem 0 0'
  })

  css.add('.coc input::after', {
    content: '""',
    display: 'inline-block',
    width: '1.5rem',
    height: '1.5rem',
    'vertical-align': 'middle',
    background: 'url(/assets/checkbox.svg)',
    cursor: 'pointer'
  })

  css.add('.coc input:checked::after', {
    'background-position': 'right'
  })

  css.add('.coc a', {
    color: '#666'
  })

  css.add('.coc a:hover', {
    'background-color': '#666',
    'text-decoration': 'none',
    color: '#fff'
  })

  css.add('p', {
    'font-size': iframe ? '1.2rem' : '1.5rem',
    'margin': iframe ? '0 0 .5rem' : '.5rem 0'
  })

  if (iframe) {
    css.add('p.status', {
      'font-size': '1.1rem'
    })
  }

  css.add('select', {
    'background': 'none'
  })

  css.add('button, .form-item', {
    'font-size': '1.2rem',
    'margin-top': iframe ? '.5rem' : '1rem',
    'vertical-align': 'middle',
    'display': 'block',
    'text-align': 'center',
    'box-sizing': 'border-box',
    'width': '100%',
    'padding': '.9rem'
  })

  css.add('button', {
    'color': '#fff',
    'font-weight': 'bold',
    'border-width': 0,
    'background': red,
    'text-transform': 'uppercase',
    'cursor': 'pointer',
    'appearence': 'none',
    '-webkit-appearence': 'none',
    'outline': '0',
    'transition': 'background-color 150ms ease-in, color 150ms ease-in'
  })

  css.add('button.loading', {
    'pointer-events': 'none'
  })

  css.add('button:disabled', {
    'color': '#9B9B9B',
    'background-color': '#D6D6D6',
    'cursor': 'default',
    'pointer-events': 'none'
  })

  css.add('button.error', {
    'background-color': '#F4001E',
    'text-transform': 'none'
  })

  css.add('button.success:disabled', {
    'color': '#fff',
    'background-color': '#68C200'
  })

  css.add('button:not(.disabled):active', {
    'background-color': '#7A002F'
  })

  css.add('b', {
    'transition': 'transform 150ms ease-in'
  })

  css.add('b.grow', {
    'transform': 'scale(1.3)'
  })

  css.add('form', {
    'margin-top': iframe ? '1rem' : '2rem',
    'margin-bottom': '0'
  })

  css.add('input', {
    'color': '#9B9B9B',
    'border': '.1rem solid #D6D6D6'
  })

  if (iframe) {
    css.add('button, .form-item', {
      'height': '2.8rem',
      'line-height': '2.8rem',
      'padding': 0,
      'font-size': '1.1rem'
    })
  }

  css.add('input:focus', {
    'color': '#666',
    'border-color': '#999',
    'outline': '0'
  })

  if (active) {
    css.add('.active', {
      'color': red
    })
  }

  css.add('p.signin', {
    'padding': '1rem 0 1rem',
    'font-size': '1.1rem'
  })

  css.add('p.signin a', {
    'color': red,
    'text-decoration': 'none'
  })

  css.add('p.signin a:hover', {
    'background-color': '#EE5050',
    color: '#fff'
  })

  if (!iframe) {
    css.add('footer', {
      'color': '#D6D6D6',
      'font-size': '1.1rem',
      'margin': '20rem auto 0',
      'width': '30rem',
      'text-align': 'center'
    })

    css.add('footer a', {
      'color': '#9B9B9B',
      'text-decoration': 'none',
      'border-bottom': '.1rem solid #9B9B9B'
    })

    css.add('footer a:hover', {
      'color': '#fff',
      'background-color': '#9B9B9B'
    })
  }

  return css
}
<|endoftext|>


<commit_before>import dom from 'vd'

export default function splash ({ path, name, org, coc, logo, active, total, channels, large, iframe, gcaptcha_sitekey }){
  let div = dom('.splash',
    !iframe && dom('.logos',
      logo && dom('.logo.org'),
      dom('.logo.slack')
    ),
    dom('p',
      'Rejoignez ', dom('b', name),
      // mention single single-channel inline
      channels && channels.length === 1 && dom('span', ' #', channels[0]),
      ' sur Slack.'
    ),
    dom('p.status',
      active
        ? [
          dom('b.active', active), ' utilisateurs connectés et ',
          dom('b.total', total), ' inscrits.'
        ]
        : [dom('b.total', total), ' inscrits jusqu\'à présent.']
    ),
    dom('form id=invite',
      channels && (
        channels.length > 1
          // channel selection when there are multiple
          ? dom('select.form-item name=channel',
              channels.map(channel => {
                return dom('option', { value: channel, text: channel })
              })
            )
          // otherwise a fixed channel
          : dom('input type=hidden name=channel', { value: channels[0] })
      ),
      dom('input.form-item type=email name=email placeholder=vous@votredomaine.com '
        + (!iframe ? 'autofocus' : '')),
      dom('br'),
      dom(`div class="g-recaptcha" data-sitekey="${gcaptcha_sitekey}"`),
      coc && dom('.coc',
        dom('label',
          dom('input type=checkbox name=coc value=1'),
          'J\'accepte les règles de conduite',
          dom('a', { href: coc, target: '_blank' }, 'Règles de conduite'),
          '.'
        )
      ),
      dom('button.loading', 'Recevoir mon invitation')
    ),
    dom('p.signin',
      'or ',
      dom(`a href=https://${org}.slack.com target=_top`, 's\'identifier'),
      '.'
    ),
    !iframe && dom('footer',
      'powered by ',
      dom('a href=http://rauchg.com/slackin target=_blank', 'slackin')
    ),
    style({ logo, active, large, iframe }),
    // xxx: single build
    dom('script', `
      data = {};
      data.path = ${JSON.stringify(path)};
    `),
    dom('script src=https://cdn.socket.io/socket.io-1.4.4.js'),
    dom(`script src=${path}assets/superagent.js`),
    dom(`script src=${path}assets/client.js`)
  )
  return div
}

const pink = '#E01563'

function style ({ logo, active, large, iframe } = {}){
  var css = dom.style()
  css.add('html', { 'font-size': large ? '14px' : '10px' })

  css.add('.splash', {
    'width': iframe ? '25rem' : '30rem',
    'margin': iframe ? '0' : '20rem auto',
    'text-align': 'center',
    'font-family': '"Helvetica Neue", Helvetica, Arial'
  })

  if (iframe) {
    css.add('body, html', {
      'margin': '0',
      'padding': '0',
      'background': '#FAFAFA',
      'overflow': 'hidden' // ff
    })

    css.add('.splash', {
      'box-sizing': 'border-box',
      'padding': '1rem'
    })
  }

  if (!iframe) {
    css
    .media('(max-width: 50rem)')
    .add('.splash', {
      'margin-top': '10rem'
    })
  }

  css.add('.head', {
    'margin-bottom': '4rem'
  })

  css.add('.logos', {
    'position': 'relative',
    'margin-bottom': '4rem'
  })

  if (!iframe) {
    css.add('.logo', {
      'width': '4.8rem',
      'height': '4.8rem',
      'display': 'inline-block',
      'background-size': 'cover'
    })

    css.add('.logo.slack', {
      'background-image': 'url(assets/slack.svg)'
    })

    if (logo) {
      let pw = 1 // '+' width in rem
      let lp = 3 // logos separation in rem

      css.add('.logo.org::after', {
        'position': 'absolute',
        'display': 'block',
        'content': '"+"',
        'top': '1.5rem',
        'left': '0',
        'width': '30rem',
        'text-align': 'center',
        'color': '#D6D6D6',
        'font-size': '1.5rem', // can't use rem in font shorthand IE9-10
              // http://codersblock.com/blog/font-shorthand-bug-in-ie10/
        'font-family': 'Helvetica Neue'
      })

      css.add('.logo.org', {
        'background-image': `url(${logo})`,
        'margin-right': `${lp + pw + lp}rem`
      })
    }
  }

  css.add('.coc', {
    'font-size': '1.2rem',
    padding: '1.5rem 0 .5rem',
    color: '#666'
  })

  if (iframe) {
    css.add('.coc', {
      'font-size': '1.1rem',
      'padding-top': '1rem'
    })

    css.add('.coc input', {
      position: 'relative',
      top: '-.2rem'
    })
  }

  css.add('.coc label', {
    cursor: 'pointer'
  })

  css.add('.coc input', {
    'appearance': 'none',
    '-webkit-appearance': 'none',
    border: 'none',
    'vertical-align': 'middle',
    margin: '0 .5rem 0 0'
  })

  css.add('.coc input::after', {
    content: '""',
    display: 'inline-block',
    width: '1.5rem',
    height: '1.5rem',
    'vertical-align': 'middle',
    background: 'url(/assets/checkbox.svg)',
    cursor: 'pointer'
  })

  css.add('.coc input:checked::after', {
    'background-position': 'right'
  })

  css.add('.coc a', {
    color: '#666'
  })

  css.add('.coc a:hover', {
    'background-color': '#666',
    'text-decoration': 'none',
    color: '#fff'
  })

  css.add('p', {
    'font-size': iframe ? '1.2rem' : '1.5rem',
    'margin': iframe ? '0 0 .5rem' : '.5rem 0'
  })

  if (iframe) {
    css.add('p.status', {
      'font-size': '1.1rem'
    })
  }

  css.add('select', {
    'background': 'none'
  })

  css.add('button, .form-item', {
    'font-size': '1.2rem',
    'margin-top': iframe ? '.5rem' : '1rem',
    'vertical-align': 'middle',
    'display': 'block',
    'text-align': 'center',
    'box-sizing': 'border-box',
    'width': '100%',
    'padding': '.9rem'
  })

  css.add('button', {
    'color': '#fff',
    'font-weight': 'bold',
    'border-width': 0,
    'background': pink,
    'text-transform': 'uppercase',
    'cursor': 'pointer',
    'appearence': 'none',
    '-webkit-appearence': 'none',
    'outline': '0',
    'transition': 'background-color 150ms ease-in, color 150ms ease-in'
  })

  css.add('button.loading', {
    'pointer-events': 'none'
  })

  css.add('button:disabled', {
    'color': '#9B9B9B',
    'background-color': '#D6D6D6',
    'cursor': 'default',
    'pointer-events': 'none'
  })

  css.add('button.error', {
    'background-color': '#F4001E',
    'text-transform': 'none'
  })

  css.add('button.success:disabled', {
    'color': '#fff',
    'background-color': '#68C200'
  })

  css.add('button:not(.disabled):active', {
    'background-color': '#7A002F'
  })

  css.add('b', {
    'transition': 'transform 150ms ease-in'
  })

  css.add('b.grow', {
    'transform': 'scale(1.3)'
  })

  css.add('form', {
    'margin-top': iframe ? '1rem' : '2rem',
    'margin-bottom': '0'
  })

  css.add('input', {
    'color': '#9B9B9B',
    'border': '.1rem solid #D6D6D6'
  })

  if (iframe) {
    css.add('button, .form-item', {
      'height': '2.8rem',
      'line-height': '2.8rem',
      'padding': 0,
      'font-size': '1.1rem'
    })
  }

  css.add('input:focus', {
    'color': '#666',
    'border-color': '#999',
    'outline': '0'
  })

  if (active) {
    css.add('.active', {
      'color': pink
    })
  }

  css.add('p.signin', {
    'padding': '1rem 0 1rem',
    'font-size': '1.1rem'
  })

  css.add('p.signin a', {
    'color': pink,
    'text-decoration': 'none'
  })

  css.add('p.signin a:hover', {
    'background-color': '#E01563',
    color: '#fff'
  })

  if (!iframe) {
    css.add('footer', {
      'color': '#D6D6D6',
      'font-size': '1.1rem',
      'margin': '20rem auto 0',
      'width': '30rem',
      'text-align': 'center'
    })

    css.add('footer a', {
      'color': '#9B9B9B',
      'text-decoration': 'none',
      'border-bottom': '.1rem solid #9B9B9B'
    })

    css.add('footer a:hover', {
      'color': '#fff',
      'background-color': '#9B9B9B'
    })
  }

  return css
}
<commit_msg>Forgotten word fix<commit_after>import dom from 'vd'

export default function splash ({ path, name, org, coc, logo, active, total, channels, large, iframe, gcaptcha_sitekey }){
  let div = dom('.splash',
    !iframe && dom('.logos',
      logo && dom('.logo.org'),
      dom('.logo.slack')
    ),
    dom('p',
      'Rejoignez ', dom('b', name),
      // mention single single-channel inline
      channels && channels.length === 1 && dom('span', ' #', channels[0]),
      ' sur Slack.'
    ),
    dom('p.status',
      active
        ? [
          dom('b.active', active), ' utilisateurs connectés et ',
          dom('b.total', total), ' inscrits.'
        ]
        : [dom('b.total', total), ' inscrits jusqu\'à présent.']
    ),
    dom('form id=invite',
      channels && (
        channels.length > 1
          // channel selection when there are multiple
          ? dom('select.form-item name=channel',
              channels.map(channel => {
                return dom('option', { value: channel, text: channel })
              })
            )
          // otherwise a fixed channel
          : dom('input type=hidden name=channel', { value: channels[0] })
      ),
      dom('input.form-item type=email name=email placeholder=vous@votredomaine.com '
        + (!iframe ? 'autofocus' : '')),
      dom('br'),
      dom(`div class="g-recaptcha" data-sitekey="${gcaptcha_sitekey}"`),
      coc && dom('.coc',
        dom('label',
          dom('input type=checkbox name=coc value=1'),
          'J\'accepte les règles de conduite',
          dom('a', { href: coc, target: '_blank' }, 'Règles de conduite'),
          '.'
        )
      ),
      dom('button.loading', 'Recevoir mon invitation')
    ),
    dom('p.signin',
      'ou ',
      dom(`a href=https://${org}.slack.com target=_top`, 's\'identifier'),
      '.'
    ),
    !iframe && dom('footer',
      'powered by ',
      dom('a href=http://rauchg.com/slackin target=_blank', 'slackin')
    ),
    style({ logo, active, large, iframe }),
    // xxx: single build
    dom('script', `
      data = {};
      data.path = ${JSON.stringify(path)};
    `),
    dom('script src=https://cdn.socket.io/socket.io-1.4.4.js'),
    dom(`script src=${path}assets/superagent.js`),
    dom(`script src=${path}assets/client.js`)
  )
  return div
}

const pink = '#E01563'

function style ({ logo, active, large, iframe } = {}){
  var css = dom.style()
  css.add('html', { 'font-size': large ? '14px' : '10px' })

  css.add('.splash', {
    'width': iframe ? '25rem' : '30rem',
    'margin': iframe ? '0' : '20rem auto',
    'text-align': 'center',
    'font-family': '"Helvetica Neue", Helvetica, Arial'
  })

  if (iframe) {
    css.add('body, html', {
      'margin': '0',
      'padding': '0',
      'background': '#FAFAFA',
      'overflow': 'hidden' // ff
    })

    css.add('.splash', {
      'box-sizing': 'border-box',
      'padding': '1rem'
    })
  }

  if (!iframe) {
    css
    .media('(max-width: 50rem)')
    .add('.splash', {
      'margin-top': '10rem'
    })
  }

  css.add('.head', {
    'margin-bottom': '4rem'
  })

  css.add('.logos', {
    'position': 'relative',
    'margin-bottom': '4rem'
  })

  if (!iframe) {
    css.add('.logo', {
      'width': '4.8rem',
      'height': '4.8rem',
      'display': 'inline-block',
      'background-size': 'cover'
    })

    css.add('.logo.slack', {
      'background-image': 'url(assets/slack.svg)'
    })

    if (logo) {
      let pw = 1 // '+' width in rem
      let lp = 3 // logos separation in rem

      css.add('.logo.org::after', {
        'position': 'absolute',
        'display': 'block',
        'content': '"+"',
        'top': '1.5rem',
        'left': '0',
        'width': '30rem',
        'text-align': 'center',
        'color': '#D6D6D6',
        'font-size': '1.5rem', // can't use rem in font shorthand IE9-10
              // http://codersblock.com/blog/font-shorthand-bug-in-ie10/
        'font-family': 'Helvetica Neue'
      })

      css.add('.logo.org', {
        'background-image': `url(${logo})`,
        'margin-right': `${lp + pw + lp}rem`
      })
    }
  }

  css.add('.coc', {
    'font-size': '1.2rem',
    padding: '1.5rem 0 .5rem',
    color: '#666'
  })

  if (iframe) {
    css.add('.coc', {
      'font-size': '1.1rem',
      'padding-top': '1rem'
    })

    css.add('.coc input', {
      position: 'relative',
      top: '-.2rem'
    })
  }

  css.add('.coc label', {
    cursor: 'pointer'
  })

  css.add('.coc input', {
    'appearance': 'none',
    '-webkit-appearance': 'none',
    border: 'none',
    'vertical-align': 'middle',
    margin: '0 .5rem 0 0'
  })

  css.add('.coc input::after', {
    content: '""',
    display: 'inline-block',
    width: '1.5rem',
    height: '1.5rem',
    'vertical-align': 'middle',
    background: 'url(/assets/checkbox.svg)',
    cursor: 'pointer'
  })

  css.add('.coc input:checked::after', {
    'background-position': 'right'
  })

  css.add('.coc a', {
    color: '#666'
  })

  css.add('.coc a:hover', {
    'background-color': '#666',
    'text-decoration': 'none',
    color: '#fff'
  })

  css.add('p', {
    'font-size': iframe ? '1.2rem' : '1.5rem',
    'margin': iframe ? '0 0 .5rem' : '.5rem 0'
  })

  if (iframe) {
    css.add('p.status', {
      'font-size': '1.1rem'
    })
  }

  css.add('select', {
    'background': 'none'
  })

  css.add('button, .form-item', {
    'font-size': '1.2rem',
    'margin-top': iframe ? '.5rem' : '1rem',
    'vertical-align': 'middle',
    'display': 'block',
    'text-align': 'center',
    'box-sizing': 'border-box',
    'width': '100%',
    'padding': '.9rem'
  })

  css.add('button', {
    'color': '#fff',
    'font-weight': 'bold',
    'border-width': 0,
    'background': pink,
    'text-transform': 'uppercase',
    'cursor': 'pointer',
    'appearence': 'none',
    '-webkit-appearence': 'none',
    'outline': '0',
    'transition': 'background-color 150ms ease-in, color 150ms ease-in'
  })

  css.add('button.loading', {
    'pointer-events': 'none'
  })

  css.add('button:disabled', {
    'color': '#9B9B9B',
    'background-color': '#D6D6D6',
    'cursor': 'default',
    'pointer-events': 'none'
  })

  css.add('button.error', {
    'background-color': '#F4001E',
    'text-transform': 'none'
  })

  css.add('button.success:disabled', {
    'color': '#fff',
    'background-color': '#68C200'
  })

  css.add('button:not(.disabled):active', {
    'background-color': '#7A002F'
  })

  css.add('b', {
    'transition': 'transform 150ms ease-in'
  })

  css.add('b.grow', {
    'transform': 'scale(1.3)'
  })

  css.add('form', {
    'margin-top': iframe ? '1rem' : '2rem',
    'margin-bottom': '0'
  })

  css.add('input', {
    'color': '#9B9B9B',
    'border': '.1rem solid #D6D6D6'
  })

  if (iframe) {
    css.add('button, .form-item', {
      'height': '2.8rem',
      'line-height': '2.8rem',
      'padding': 0,
      'font-size': '1.1rem'
    })
  }

  css.add('input:focus', {
    'color': '#666',
    'border-color': '#999',
    'outline': '0'
  })

  if (active) {
    css.add('.active', {
      'color': pink
    })
  }

  css.add('p.signin', {
    'padding': '1rem 0 1rem',
    'font-size': '1.1rem'
  })

  css.add('p.signin a', {
    'color': pink,
    'text-decoration': 'none'
  })

  css.add('p.signin a:hover', {
    'background-color': '#E01563',
    color: '#fff'
  })

  if (!iframe) {
    css.add('footer', {
      'color': '#D6D6D6',
      'font-size': '1.1rem',
      'margin': '20rem auto 0',
      'width': '30rem',
      'text-align': 'center'
    })

    css.add('footer a', {
      'color': '#9B9B9B',
      'text-decoration': 'none',
      'border-bottom': '.1rem solid #9B9B9B'
    })

    css.add('footer a:hover', {
      'color': '#fff',
      'background-color': '#9B9B9B'
    })
  }

  return css
}
<|endoftext|>


<commit_before>const pathPosix = require("path-posix");
const joinURL = require("url-join");
const { merge } = require("../merge.js");
const { handleResponseCode, processResponsePayload, processGlobFilter } = require("../response.js");
const { normaliseHREF, normalisePath } = require("../url.js");
const { getSingleValue, getValueForKey, parseXML, propsToStat } = require("./dav.js");
const { encodePath, prepareRequestOptions, request } = require("../request.js");

function getDirectoryContents(remotePath, options) {
    // Join the URL and path for the request
    const requestOptions = {
        url: joinURL(options.remoteURL, encodePath(remotePath), "/"),
        method: "PROPFIND",
        headers: {
            Accept: "text/plain",
            Depth: options.deep ? "infinity" : 1
        },
        responseType: "text"
    };
    let response = null;
    prepareRequestOptions(requestOptions, options);
    return request(requestOptions)
        .then(handleResponseCode)
        .then(res => {
            response = res;
            return res.data;
        })
        .then(parseXML)
        .then(result => getDirectoryFiles(result, options.remotePath, remotePath, options.details))
        .then(files => processResponsePayload(response, files, options.details))
        .then(files => {
            if (options.glob && options.glob.pattern) {
                return processGlobFilter(files, options.glob);
            }
            return files;
        });
}

function getDirectoryFiles(result, serverBasePath, requestPath, isDetailed = false) {
    const remoteTargetPath = pathPosix.join(serverBasePath, requestPath, "/");
    const serverBase = pathPosix.join(serverBasePath, "/");
    // Extract the response items (directory contents)
    const multiStatus = getValueForKey("multistatus", result);
    const responseItems = getValueForKey("response", multiStatus);
    return (
        responseItems
            // Filter out the item pointing to the current directory (not needed)
            .filter(item => {
                let href = getSingleValue(getValueForKey("href", item));
                href = pathPosix.join(normalisePath(normaliseHREF(href)), "/");
                return href !== serverBase && href !== remoteTargetPath;
            })
            // Map all items to a consistent output structure (results)
            .map(item => {
                // HREF is the file path (in full)
                let href = getSingleValue(getValueForKey("href", item));
                href = normaliseHREF(href);
                // Each item should contain a stat object
                const propStat = getSingleValue(getValueForKey("propstat", item));
                const props = getSingleValue(getValueForKey("prop", propStat));
                // Process the true full filename (minus the base server path)
                const filename =
                    serverBase === "/" ? normalisePath(href) : normalisePath(pathPosix.relative(serverBase, href));
                return propsToStat(props, filename, isDetailed);
            })
    );
}

module.exports = {
    getDirectoryContents
};
<commit_msg>alphabetised<commit_after>const pathPosix = require("path-posix");
const joinURL = require("url-join");
const { merge } = require("../merge.js");
const { handleResponseCode, processGlobFilter, processResponsePayload } = require("../response.js");
const { normaliseHREF, normalisePath } = require("../url.js");
const { getSingleValue, getValueForKey, parseXML, propsToStat } = require("./dav.js");
const { encodePath, prepareRequestOptions, request } = require("../request.js");

function getDirectoryContents(remotePath, options) {
    // Join the URL and path for the request
    const requestOptions = {
        url: joinURL(options.remoteURL, encodePath(remotePath), "/"),
        method: "PROPFIND",
        headers: {
            Accept: "text/plain",
            Depth: options.deep ? "infinity" : 1
        },
        responseType: "text"
    };
    let response = null;
    prepareRequestOptions(requestOptions, options);
    return request(requestOptions)
        .then(handleResponseCode)
        .then(res => {
            response = res;
            return res.data;
        })
        .then(parseXML)
        .then(result => getDirectoryFiles(result, options.remotePath, remotePath, options.details))
        .then(files => processResponsePayload(response, files, options.details))
        .then(files => {
            if (options.glob && options.glob.pattern) {
                return processGlobFilter(files, options.glob);
            }
            return files;
        });
}

function getDirectoryFiles(result, serverBasePath, requestPath, isDetailed = false) {
    const remoteTargetPath = pathPosix.join(serverBasePath, requestPath, "/");
    const serverBase = pathPosix.join(serverBasePath, "/");
    // Extract the response items (directory contents)
    const multiStatus = getValueForKey("multistatus", result);
    const responseItems = getValueForKey("response", multiStatus);
    return (
        responseItems
            // Filter out the item pointing to the current directory (not needed)
            .filter(item => {
                let href = getSingleValue(getValueForKey("href", item));
                href = pathPosix.join(normalisePath(normaliseHREF(href)), "/");
                return href !== serverBase && href !== remoteTargetPath;
            })
            // Map all items to a consistent output structure (results)
            .map(item => {
                // HREF is the file path (in full)
                let href = getSingleValue(getValueForKey("href", item));
                href = normaliseHREF(href);
                // Each item should contain a stat object
                const propStat = getSingleValue(getValueForKey("propstat", item));
                const props = getSingleValue(getValueForKey("prop", propStat));
                // Process the true full filename (minus the base server path)
                const filename =
                    serverBase === "/" ? normalisePath(href) : normalisePath(pathPosix.relative(serverBase, href));
                return propsToStat(props, filename, isDetailed);
            })
    );
}

module.exports = {
    getDirectoryContents
};
<|endoftext|>


<commit_before>// Write a function that returns the sum of the first two parameters

function square(a, b) {
  // Your code goes here.
}

module.exports = square;<commit_msg>fix the square method<commit_after>// Write a function that returns the square of the first parameter

function square(a) {
  return a * a;
}

module.exports = square;<|endoftext|>