<commit_before>const request = require('request');
const bunyan = require('bunyan');
const config = require('config');
const getTicker = require('./lib/get-ticker');
const getMarketHistory = require('./lib/get-market-history');
const helper = require('./helper');
const _ = require('lodash');
const fs = require('fs');
const tradeStub = require('./lib/trade-stub');
const Account = require('./lib/account');

const log = bunyan.createLogger({ name: 'evaluate-strategy' });

const getCrossovers = market => new Promise(async (resolveGetCrossovers, rejectGetCrossovers) => request(`${config.get('bittrexCache.getcrossoverurl')}?market=${market}`, (error, response) => {
  if (error) {
    return rejectGetCrossovers(error);
  }

  if (response.body === undefined) {
    return rejectGetCrossovers(new Error('Empty body'));
  }

  let jsonBody;
  try {
    jsonBody = JSON.parse(response.body);
  } catch (jsonParseError) {
    return rejectGetCrossovers(jsonParseError);
  }

  return resolveGetCrossovers(jsonBody.result);
}));

(async () => {
  const tasks = [
    getTicker(config.get('bittrexMarket')),
    getCrossovers(config.get('bittrexMarket')),
  ];

  const [ticker, crossoverData] = await Promise.all(tasks);
  if (crossoverData.length === 0) {
    log.error(`No crossover data from ${config.get('bittrexCache.getcrossoverurl')}`);
    process.exit();
  }

  let tradeAmount = 1000; // How much currency you have to trade
  const account = new Account(config.get('sienaAccount.baseCurrency'), tradeAmount);
  const buySellPoints = [];

  const strategyResult = crossoverData.reduce((accumatedPosition, crossoverPoint) => {
    const position = accumatedPosition;
    const timeSinceLastTrade = helper.cleanBittrexTimestamp(crossoverPoint.timestamp) -
      position.lastTradeTime;

    // Only buy if the trend is up, AND
    // you have some amount to trade, AND
    // it has been atleast sometime since your last trade

    log.info(`trend:${crossoverPoint.trend}, crossoverTime: ${crossoverPoint.timestamp}, market:${(crossoverPoint.market || 'nevermind')}, balance:${position.account.getBalanceNumber()}, timeSinceLastTrade: ${helper.millisecondsToHours(timeSinceLastTrade)}, lastBuyPrice: ${(position.lastBuyPrice || 'nevermind')}, bidPrice: ${crossoverPoint.bidPrice}, securityBalance: ${position.security}`);
    if (
      position.account.getBalanceNumber() > 1 &&
      ((
        crossoverPoint.market === 'VOLATILE-LOW' &&
        (crossoverPoint.movingAverageLong - crossoverPoint.movingAverageShort) > 0.2
      ) || (
          crossoverPoint.market !== 'BEAR' &&
          position.lastTrade === 'SELL-LOW' &&
          position.lastSellPrice > crossoverPoint.askPrice
        ))
    ) {
      log.info(`position.lastTrade: ${position.lastTrade}`);
      log.info(`Time since last trade : ${helper.millisecondsToHours(timeSinceLastTrade)}`);
      // Buy at ask price
      // Commission is in USDT
      const quantity = position.account.getTradeAmount() / crossoverPoint.askPrice;
      const commission = tradeStub.getCommission(quantity, crossoverPoint.askPrice);
      const buyLesserQuantity = (position.account.getTradeAmount() - commission)
        / crossoverPoint.askPrice;
      const trade = tradeStub.buy(buyLesserQuantity, crossoverPoint.askPrice);

      position.security = trade.security;
      position.account.debit(trade.total);
      position.lastTradeTime = helper.cleanBittrexTimestamp(crossoverPoint.timestamp);
      position.lastBuyPrice = crossoverPoint.askPrice;
      position.lastTrade = 'BUY';
      buySellPoints.push(`${helper.cleanBittrexTimestamp(crossoverPoint.timestamp)},${crossoverPoint.askPrice},1`);
    } else if (
      (
        (
          // Dont sell in a bull market
          crossoverPoint.market !== 'BULL' &&
          // Make sure the sell price is some percentage higher than the buy price
          crossoverPoint.bidPrice > (position.lastBuyPrice + (0.03 * position.lastBuyPrice))
        ) ||
        (
          // Market has turned bear, cut your loss short
          crossoverPoint.market === 'BEAR' &&
          crossoverPoint.bidPrice < (position.lastBuyPrice - (0.01 * position.lastBuyPrice))
        )
      ) && position.security > 0
        && position.lastTrade === 'BUY') {
      // Sell at the bid price
      // Commission is in USDT
      const quantity = position.security;
      const trade = tradeStub.sell(quantity, crossoverPoint.bidPrice);
      position.account.credit(trade.total);
      position.security = 0;
      position.lastTradeTime = helper.cleanBittrexTimestamp(crossoverPoint.timestamp);
      if (crossoverPoint.market === 'BEAR') {
        position.lastTrade = 'SELL-LOW';
      } else {
        position.lastTrade = 'SELL-HIGH';
      }
      position.lastSellPrice = crossoverPoint.bidPrice;
      buySellPoints.push(`${helper.cleanBittrexTimestamp(crossoverPoint.timestamp)},${crossoverPoint.bidPrice},0`);
    }

    return (position);
  }, { security: 0, account, lastTradeTime: null });
  if (strategyResult.security > 0) {
    // Sell off any security
    const trade = tradeStub.sell(strategyResult.security, ticker.Ask);
    strategyResult.account.credit(trade.total);
  }
  log.info('Simulating strategy less buy and sale');
  // Buy it during the first value of the crossover
  const nonStrategyBuyTrade = tradeStub.buy(
    tradeAmount / crossoverData[0].askPrice,
    crossoverData[0].askPrice);
  const security = nonStrategyBuyTrade.security;

  // Sell it for the current asking price
  const nonStrategySellTrade = tradeStub.sell(security, ticker.Ask);
  tradeAmount = nonStrategySellTrade.total;

  // Generate a file with all the buy and sell points.
  const strategyResultDataFile = 'strategyResultData.txt';
  const strategyResultData = buySellPoints.join('\n');

  log.info(`Current balance based on strategy : ${strategyResult.account.getBalanceNumber()}, Current balance if you just bought and sold : ${tradeAmount}`);

  const tradeHistoryDataFile = 'tradeHistory.txt';

  // Get the market history to plot the data
  const toTimestamp = new Date().getTime();
  const fromTimestamp24 = toTimestamp - (3600000 * 168); // 24 hours

  const marketHistoryData = await getMarketHistory(config.get('bittrexMarket'), fromTimestamp24, toTimestamp, 'bittrexCache');
  const filteredData = helper.getSoldPricesBetweenPeriod(marketHistoryData,
    fromTimestamp24,
    toTimestamp);

  const timestamps = _.map(filteredData, object =>
    helper.cleanBittrexTimestamp(object.TimeStamp));
  log.info(`Timestamps between ${new Date(Math.min(...timestamps))} and ${new Date(Math.max(...timestamps))} for about ${(Math.max(...timestamps) - Math.min(...timestamps)) / (1000 * 60 * 60)} hours`);

  const tradeHistoryData = _.map(filteredData, object => `${helper.cleanBittrexTimestamp(object.TimeStamp)},${object.Price}`).join('\n');

  const fileWriteTasks = [
    new Promise(async resolveWrite => fs.writeFile(
      tradeHistoryDataFile,
      tradeHistoryData,
      resolveWrite)),
    new Promise(async resolveWrite => fs.writeFile(
      strategyResultDataFile,
      strategyResultData,
      resolveWrite)),
  ];

  await Promise.all(fileWriteTasks);

  log.info('Done!');
})();
<commit_msg>Adjusting test strategy values to get the best returns<commit_after>const request = require('request');
const bunyan = require('bunyan');
const config = require('config');
const getTicker = require('./lib/get-ticker');
const getMarketHistory = require('./lib/get-market-history');
const helper = require('./helper');
const _ = require('lodash');
const fs = require('fs');
const tradeStub = require('./lib/trade-stub');
const Account = require('./lib/account');

const log = bunyan.createLogger({ name: 'evaluate-strategy' });

const getCrossovers = market => new Promise(async (resolveGetCrossovers, rejectGetCrossovers) => request(`${config.get('bittrexCache.getcrossoverurl')}?market=${market}`, (error, response) => {
  if (error) {
    return rejectGetCrossovers(error);
  }

  if (response.body === undefined) {
    return rejectGetCrossovers(new Error('Empty body'));
  }

  let jsonBody;
  try {
    jsonBody = JSON.parse(response.body);
  } catch (jsonParseError) {
    return rejectGetCrossovers(jsonParseError);
  }

  return resolveGetCrossovers(jsonBody.result);
}));

(async () => {
  const tasks = [
    getTicker(config.get('bittrexMarket')),
    getCrossovers(config.get('bittrexMarket')),
  ];

  const [ticker, crossoverData] = await Promise.all(tasks);
  if (crossoverData.length === 0) {
    log.error(`No crossover data from ${config.get('bittrexCache.getcrossoverurl')}`);
    process.exit();
  }

  let tradeAmount = 1000; // How much currency you have to trade
  const account = new Account(config.get('sienaAccount.baseCurrency'), tradeAmount);
  const buySellPoints = [];

  const strategyResult = crossoverData.reduce((accumatedPosition, crossoverPoint) => {
    const position = accumatedPosition;
    const timeSinceLastTrade = helper.cleanBittrexTimestamp(crossoverPoint.timestamp) -
      position.lastTradeTime;

    // Only buy if the trend is up, AND
    // you have some amount to trade, AND
    // it has been atleast sometime since your last trade

    log.info(`trend:${crossoverPoint.trend}, crossoverTime: ${crossoverPoint.timestamp}, market:${(crossoverPoint.market || 'nevermind')}, balance:${position.account.getBalanceNumber()}, timeSinceLastTrade: ${helper.millisecondsToHours(timeSinceLastTrade)}, lastBuyPrice: ${(position.lastBuyPrice || 'nevermind')}, bidPrice: ${crossoverPoint.bidPrice}, securityBalance: ${position.security}`);
    if (
      position.account.getBalanceNumber() > 1 &&
      ((
        crossoverPoint.market === 'VOLATILE-LOW' &&
        (crossoverPoint.movingAverageLong - crossoverPoint.movingAverageShort) > 0.2
      ) || (
          crossoverPoint.market !== 'BEAR' &&
          position.lastTrade === 'SELL-LOW' &&
          position.lastSellPrice > crossoverPoint.askPrice
        ))
    ) {
      log.info(`position.lastTrade: ${position.lastTrade}`);
      log.info(`Time since last trade : ${helper.millisecondsToHours(timeSinceLastTrade)}`);
      // Buy at ask price
      // Commission is in USDT
      const quantity = position.account.getTradeAmount() / crossoverPoint.askPrice;
      const commission = tradeStub.getCommission(quantity, crossoverPoint.askPrice);
      const buyLesserQuantity = (position.account.getTradeAmount() - commission)
        / crossoverPoint.askPrice;
      const trade = tradeStub.buy(buyLesserQuantity, crossoverPoint.askPrice);

      position.security = trade.security;
      position.account.debit(trade.total);
      position.lastTradeTime = helper.cleanBittrexTimestamp(crossoverPoint.timestamp);
      position.lastBuyPrice = crossoverPoint.askPrice;
      position.lastTrade = 'BUY';
      log.info(`Traded on ${new Date(position.lastTradeTime)}`);
      buySellPoints.push(`${helper.cleanBittrexTimestamp(crossoverPoint.timestamp)},${crossoverPoint.askPrice},1`);
    } else if (
      (
        (
          // Dont sell in a bull market
          crossoverPoint.market !== 'BULL' &&
          // Make sure the sell price is some percentage higher than the buy price
          crossoverPoint.bidPrice > (position.lastBuyPrice + (0.03 * position.lastBuyPrice))
        ) ||
        (
          // Market has turned bear, cut your loss short
          crossoverPoint.market === 'BEAR' &&
          crossoverPoint.bidPrice < (position.lastBuyPrice - (0.1 * position.lastBuyPrice))
        )
      ) && position.security > 0
        && position.lastTrade === 'BUY') {
      // Sell at the bid price
      // Commission is in USDT
      const quantity = position.security;
      const trade = tradeStub.sell(quantity, crossoverPoint.bidPrice);
      position.account.credit(trade.total);
      position.security = 0;
      position.lastTradeTime = helper.cleanBittrexTimestamp(crossoverPoint.timestamp);
      if (crossoverPoint.market === 'BEAR') {
        position.lastTrade = 'SELL-LOW';
      } else {
        position.lastTrade = 'SELL-HIGH';
      }
      position.lastSellPrice = crossoverPoint.bidPrice;
      log.info(`Traded on ${new Date(position.lastTradeTime)}`);
      buySellPoints.push(`${helper.cleanBittrexTimestamp(crossoverPoint.timestamp)},${crossoverPoint.bidPrice},0`);
    }

    return (position);
  }, { security: 0, account, lastTradeTime: null });
  if (strategyResult.security > 0) {
    // Sell off any security
    const trade = tradeStub.sell(strategyResult.security, ticker.Ask);
    strategyResult.account.credit(trade.total);
  }
  log.info('Simulating strategy less buy and sale');
  // Buy it during the first value of the crossover
  const nonStrategyBuyTrade = tradeStub.buy(
    tradeAmount / crossoverData[0].askPrice,
    crossoverData[0].askPrice);
  const security = nonStrategyBuyTrade.security;

  // Sell it for the current asking price
  const nonStrategySellTrade = tradeStub.sell(security, ticker.Ask);
  tradeAmount = nonStrategySellTrade.total;

  // Generate a file with all the buy and sell points.
  const strategyResultDataFile = 'strategyResultData.txt';
  const strategyResultData = buySellPoints.join('\n');

  log.info(`Current balance based on strategy : ${strategyResult.account.getBalanceNumber()}, Current balance if you just bought and sold : ${tradeAmount}`);

  const tradeHistoryDataFile = 'tradeHistory.txt';

  // Get the market history to plot the data
  const toTimestamp = new Date().getTime();
  const fromTimestamp24 = toTimestamp - (3600000 * 168); // 24 hours

  const marketHistoryData = await getMarketHistory(config.get('bittrexMarket'), fromTimestamp24, toTimestamp, 'bittrexCache');
  const filteredData = helper.getSoldPricesBetweenPeriod(marketHistoryData,
    fromTimestamp24,
    toTimestamp);

  const timestamps = _.map(filteredData, object =>
    helper.cleanBittrexTimestamp(object.TimeStamp));
  log.info(`Timestamps between ${new Date(Math.min(...timestamps))} and ${new Date(Math.max(...timestamps))} for about ${(Math.max(...timestamps) - Math.min(...timestamps)) / (1000 * 60 * 60)} hours`);

  const tradeHistoryData = _.map(filteredData, object => `${helper.cleanBittrexTimestamp(object.TimeStamp)},${object.Price}`).join('\n');

  const fileWriteTasks = [
    new Promise(async resolveWrite => fs.writeFile(
      tradeHistoryDataFile,
      tradeHistoryData,
      resolveWrite)),
    new Promise(async resolveWrite => fs.writeFile(
      strategyResultDataFile,
      strategyResultData,
      resolveWrite)),
  ];

  await Promise.all(fileWriteTasks);

  log.info('Done!');
})();
<|endoftext|>


<commit_before>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  AnalyticsRequest,
  AnalyticsContext,
  AnalyticsEventMeta,
  AnalyticsEvent,
  EventParams,
  EventOriginator,
} from '../proto/api_messages';
import {createElement} from '../utils/dom';
import {feArgs, feUrl} from './services';
import {getOnExperiments} from './experiments';
import {parseQueryString, parseUrl} from '../utils/url';
import {setImportantStyles} from '../utils/style';
import {getUuid} from '../utils/string';
import {ClientEventManager} from './client-event-manager';

/** @const {!Object<string, string>} */
const iframeStyles = {
  display: 'none',
};

export class AnalyticsService {
  /**
   * @param {!./deps.DepsDef} deps
   */
  constructor(deps) {
    /** @private @const {!../model/doc.Doc} */
    this.doc_ = deps.doc();

    /** @private @const {!./deps.DepsDef} */
    this.deps_ = deps;

    /** @private @const {!../components/activities.ActivityPorts} */
    this.activityPorts_ = deps.activities();

    /** @private @const {!HTMLIFrameElement} */
    this.iframe_ = /** @type {!HTMLIFrameElement} */ (createElement(
      this.doc_.getWin().document,
      'iframe',
      {}
    ));

    setImportantStyles(this.iframe_, iframeStyles);

    /** @private @const {string} */
    this.src_ = feUrl('/serviceiframe');

    /** @private @const {string} */
    this.publicationId_ = deps.pageConfig().getPublicationId();

    this.args_ = feArgs({
      publicationId: this.publicationId_,
    });

    /** @private @type {!boolean} */
    this.everLogged_ = false;

    /**
     * @private @const {!AnalyticsContext}
     */
    this.context_ = new AnalyticsContext();

    this.context_.setTransactionId(getUuid());

    /** @private {?Promise<!web-activities/activity-ports.ActivityIframePort>} */
    this.serviceReady_ = null;

    /** @private {?Promise} */
    this.lastAction_ = null;

    /** @private @const {!ClientEventManager} */
    this.eventManager_ = deps.eventManager();
    this.eventManager_.registerEventListener(
      this.handleClientEvent_.bind(this)
    );
  }

  /**
   * @param {string} transactionId
   */
  setTransactionId(transactionId) {
    this.context_.setTransactionId(transactionId);
  }

  /**
   * @return {string}
   */
  getTransactionId() {
    return /** @type {string} */ (this.context_.getTransactionId());
  }

  /**
   * @return {?string}
   */
  getSku() {
    return this.context_.getSku();
  }

  /**
   * @param {string} sku
   */
  setSku(sku) {
    this.context_.setSku(sku);
  }

  /**
   * @param {!Array<string>} labels
   */
  addLabels(labels) {
    if (labels && labels.length > 0) {
      const newLabels = [].concat(this.context_.getLabelList());
      labels.forEach(label => {
        if (newLabels.indexOf(label) == -1) {
          newLabels.push(label);
        }
      });
      this.context_.setLabelList(newLabels);
    }
  }

  /**
   * @return {!HTMLIFrameElement}
   */
  getElement() {
    return this.iframe_;
  }

  /**
   * @return {string}
   * @private
   */
  getQueryString_() {
    return this.doc_.getWin().location.search;
  }

  /**
   * @return {string}
   * @private
   */
  getReferrer_() {
    return this.doc_.getWin().document.referrer;
  }

  /**
   * @private
   */
  setContext_() {
    const utmParams = parseQueryString(this.getQueryString_());
    this.context_.setReferringOrigin(parseUrl(this.getReferrer_()).origin);
    const campaign = utmParams['utm_campaign'];
    const medium = utmParams['utm_medium'];
    const source = utmParams['utm_source'];
    if (campaign) {
      this.context_.setUtmCampaign(campaign);
    }
    if (medium) {
      this.context_.setUtmMedium(medium);
    }
    if (source) {
      this.context_.setUtmSource(source);
    }
    this.addLabels(getOnExperiments(this.doc_.getWin()));
  }

  /**
   * @return {!Promise<!../components/activities.ActivityIframePort>}
   */
  start() {
    if (!this.serviceReady_) {
      // TODO(sohanirao): Potentially do this even earlier
      this.doc_.getBody().appendChild(this.getElement());
      this.serviceReady_ = this.activityPorts_
        .openIframe(this.iframe_, this.src_, this.args_)
        .then(port => {
          this.setContext_();
          return port.whenReady().then(() => port);
        });
    }
    return this.serviceReady_;
  }

  /**
   * @param {boolean} isReadyToPay
   */
  setReadyToPay(isReadyToPay) {
    this.context_.setReadyToPay(isReadyToPay);
  }

  /**
   */
  close() {
    this.doc_.getBody().removeChild(this.getElement());
  }

  /**
   * @return {!AnalyticsContext}
   */
  getContext() {
    return this.context_;
  }

  /**
   * Returns true if any logs have already be sent to the analytics server.
   * @return {boolean}
   */
  getHasLogged() {
    return this.everLogged_;
  }

  /**
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @return {!AnalyticsRequest}
   */
  createLogRequest_(event) {
    const meta = new AnalyticsEventMeta();
    meta.setEventOriginator(event.eventOriginator);
    meta.setIsFromUserAction(event.isFromUserAction);

    const request = new AnalyticsRequest();
    request.setEvent(event.eventType);
    request.setContext(this.context_);
    request.setMeta(meta);
    if (event.additionalParameters instanceof EventParams) {
      request.setParams(event.additionalParameters);
    } // Ignore event.additionalParameters.  It may have data we shouldn't log.
    return request;
  }

  /**
   * @return {boolean}
   */
  shouldLogPublisherEvents_() {
    return this.deps_.config().enableSwgAnalytics === true;
  }

  /**
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @return {boolean}
   */
  shouldAlwaysLogEvent_(event) {
    /* AMP_CLIENT events are considered publisher events and we generally only
     * log those if the publisher decided to enable publisher event logging for
     * privacy purposes.  The page load event is not private and is necessary
     * just so we know the user is in AMP, so we will log it regardless of
     * configuration.
     */
    return (
      event.eventType === AnalyticsEvent.IMPRESSION_PAGE_LOAD &&
      event.eventOriginator === EventOriginator.AMP_CLIENT
    );
  }

  /**
   *  Listens for new events from the events manager and handles logging
   * @param {!../api/client-event-manager-api.ClientEvent} event
   */
  handleClientEvent_(event) {
    //this event is just used to communicate information internally.  It should
    //not be reported to the SwG analytics service.
    if (event.eventType === AnalyticsEvent.EVENT_SUBSCRIPTION_STATE) {
      return;
    }

    if (
      ClientEventManager.isPublisherEvent(event) &&
      !this.shouldLogPublisherEvents_() &&
      !this.shouldAlwaysLogEvent_(event)
    ) {
      return;
    }
    this.lastAction_ = this.start().then(port => {
      const request = this.createLogRequest_(event);
      this.everLogged_ = true;
      port.execute(request);
    });
  }
}
<commit_msg>Set client version in logs<commit_after>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  AnalyticsRequest,
  AnalyticsContext,
  AnalyticsEventMeta,
  AnalyticsEvent,
  EventParams,
  EventOriginator,
} from '../proto/api_messages';
import {createElement} from '../utils/dom';
import {feArgs, feUrl} from './services';
import {getOnExperiments} from './experiments';
import {parseQueryString, parseUrl} from '../utils/url';
import {setImportantStyles} from '../utils/style';
import {getUuid} from '../utils/string';
import {ClientEventManager} from './client-event-manager';

/** @const {!Object<string, string>} */
const iframeStyles = {
  display: 'none',
};

export class AnalyticsService {
  /**
   * @param {!./deps.DepsDef} deps
   */
  constructor(deps) {
    /** @private @const {!../model/doc.Doc} */
    this.doc_ = deps.doc();

    /** @private @const {!./deps.DepsDef} */
    this.deps_ = deps;

    /** @private @const {!../components/activities.ActivityPorts} */
    this.activityPorts_ = deps.activities();

    /** @private @const {!HTMLIFrameElement} */
    this.iframe_ = /** @type {!HTMLIFrameElement} */ (createElement(
      this.doc_.getWin().document,
      'iframe',
      {}
    ));

    setImportantStyles(this.iframe_, iframeStyles);

    /** @private @const {string} */
    this.src_ = feUrl('/serviceiframe');

    /** @private @const {string} */
    this.publicationId_ = deps.pageConfig().getPublicationId();

    this.args_ = feArgs({
      publicationId: this.publicationId_,
    });

    /** @private @type {!boolean} */
    this.everLogged_ = false;

    /**
     * @private @const {!AnalyticsContext}
     */
    this.context_ = new AnalyticsContext();

    this.context_.setTransactionId(getUuid());

    /** @private {?Promise<!web-activities/activity-ports.ActivityIframePort>} */
    this.serviceReady_ = null;

    /** @private {?Promise} */
    this.lastAction_ = null;

    /** @private @const {!ClientEventManager} */
    this.eventManager_ = deps.eventManager();
    this.eventManager_.registerEventListener(
      this.handleClientEvent_.bind(this)
    );
  }

  /**
   * @param {string} transactionId
   */
  setTransactionId(transactionId) {
    this.context_.setTransactionId(transactionId);
  }

  /**
   * @return {string}
   */
  getTransactionId() {
    return /** @type {string} */ (this.context_.getTransactionId());
  }

  /**
   * @return {?string}
   */
  getSku() {
    return this.context_.getSku();
  }

  /**
   * @param {string} sku
   */
  setSku(sku) {
    this.context_.setSku(sku);
  }

  /**
   * @param {!Array<string>} labels
   */
  addLabels(labels) {
    if (labels && labels.length > 0) {
      const newLabels = [].concat(this.context_.getLabelList());
      labels.forEach(label => {
        if (newLabels.indexOf(label) == -1) {
          newLabels.push(label);
        }
      });
      this.context_.setLabelList(newLabels);
    }
  }

  /**
   * @return {!HTMLIFrameElement}
   */
  getElement() {
    return this.iframe_;
  }

  /**
   * @return {string}
   * @private
   */
  getQueryString_() {
    return this.doc_.getWin().location.search;
  }

  /**
   * @return {string}
   * @private
   */
  getReferrer_() {
    return this.doc_.getWin().document.referrer;
  }

  /**
   * @private
   */
  setContext_() {
    const utmParams = parseQueryString(this.getQueryString_());
    this.context_.setReferringOrigin(parseUrl(this.getReferrer_()).origin);
    const campaign = utmParams['utm_campaign'];
    const medium = utmParams['utm_medium'];
    const source = utmParams['utm_source'];
    if (campaign) {
      this.context_.setUtmCampaign(campaign);
    }
    if (medium) {
      this.context_.setUtmMedium(medium);
    }
    if (source) {
      this.context_.setUtmSource(source);
    }
    this.context_.setClientVersion('SwG $internalRuntimeVersion$');
    this.addLabels(getOnExperiments(this.doc_.getWin()));
  }

  /**
   * @return {!Promise<!../components/activities.ActivityIframePort>}
   */
  start() {
    if (!this.serviceReady_) {
      // TODO(sohanirao): Potentially do this even earlier
      this.doc_.getBody().appendChild(this.getElement());
      this.serviceReady_ = this.activityPorts_
        .openIframe(this.iframe_, this.src_, this.args_)
        .then(port => {
          this.setContext_();
          return port.whenReady().then(() => port);
        });
    }
    return this.serviceReady_;
  }

  /**
   * @param {boolean} isReadyToPay
   */
  setReadyToPay(isReadyToPay) {
    this.context_.setReadyToPay(isReadyToPay);
  }

  /**
   */
  close() {
    this.doc_.getBody().removeChild(this.getElement());
  }

  /**
   * @return {!AnalyticsContext}
   */
  getContext() {
    return this.context_;
  }

  /**
   * Returns true if any logs have already be sent to the analytics server.
   * @return {boolean}
   */
  getHasLogged() {
    return this.everLogged_;
  }

  /**
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @return {!AnalyticsRequest}
   */
  createLogRequest_(event) {
    const meta = new AnalyticsEventMeta();
    meta.setEventOriginator(event.eventOriginator);
    meta.setIsFromUserAction(event.isFromUserAction);

    const request = new AnalyticsRequest();
    request.setEvent(event.eventType);
    request.setContext(this.context_);
    request.setMeta(meta);
    if (event.additionalParameters instanceof EventParams) {
      request.setParams(event.additionalParameters);
    } // Ignore event.additionalParameters.  It may have data we shouldn't log.
    return request;
  }

  /**
   * @return {boolean}
   */
  shouldLogPublisherEvents_() {
    return this.deps_.config().enableSwgAnalytics === true;
  }

  /**
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @return {boolean}
   */
  shouldAlwaysLogEvent_(event) {
    /* AMP_CLIENT events are considered publisher events and we generally only
     * log those if the publisher decided to enable publisher event logging for
     * privacy purposes.  The page load event is not private and is necessary
     * just so we know the user is in AMP, so we will log it regardless of
     * configuration.
     */
    return (
      event.eventType === AnalyticsEvent.IMPRESSION_PAGE_LOAD &&
      event.eventOriginator === EventOriginator.AMP_CLIENT
    );
  }

  /**
   *  Listens for new events from the events manager and handles logging
   * @param {!../api/client-event-manager-api.ClientEvent} event
   */
  handleClientEvent_(event) {
    //this event is just used to communicate information internally.  It should
    //not be reported to the SwG analytics service.
    if (event.eventType === AnalyticsEvent.EVENT_SUBSCRIPTION_STATE) {
      return;
    }

    if (
      ClientEventManager.isPublisherEvent(event) &&
      !this.shouldLogPublisherEvents_() &&
      !this.shouldAlwaysLogEvent_(event)
    ) {
      return;
    }
    this.lastAction_ = this.start().then(port => {
      const request = this.createLogRequest_(event);
      this.everLogged_ = true;
      port.execute(request);
    });
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import {shouldComponentUpdate} from 'react/lib/ReactComponentWithPureRenderMixin';
import {locationHistory as location, actions, href, isActive} from '../..';
import {createStore} from './store';
import css from './App.css';

const store = createStore();

location({store, namespace: 'componentRouter'});

const navigateTo = params => event => {
  event.preventDefault();
  store.dispatch(actions.navigateTo(params));
};

const GlobalLinks = React.createClass({
  propTypes: {
    routingState: React.PropTypes.object
  },

  shouldComponentUpdate,

  render() {
    const {routingState} = this.props;

    return (

          Home

          /foo

          /bar

          /404

    );
  }
});

const componentsList = ['bla', 'baz', 'abc', 'zyx'];
const ComponentLinks = React.createClass({
  propTypes: {
    routingState: React.PropTypes.object
  },

  componentDidMount() {
    store.dispatch(actions.addDefaultParam('component', 'baz'));
    store.dispatch(actions.addDefaultParam('sorting', 'Unsorted'));
    store.dispatch(actions.addOffRecordParam('sorting'));
  },

  shouldComponentUpdate,

  componentWillUnmount() {
    store.dispatch(actions.removeParam('component'));
    store.dispatch(actions.removeParam('sorting'));
  },

  render() {
    const {routingState} = this.props;

    const sortIt = (original, direction) => {
      if (direction === 'Unsorted') return original;

      const sorted = original.slice(0).sort();

      if (direction === 'Descending') {
        sorted.reverse();
      }

      return sorted;
    };

    return (

             No sorting

             Ascending

             Descending

           (sorting changes are not going into the browser history)

        {
          sortIt(componentsList, routingState.query.sorting).map(c => (component: {c}))
        }

    );
  }
});

const Header = ({...props}) => (

      <GlobalLinks {...props} />

);

const Foo = ({...props}) => (

Foo

      <ComponentLinks {...props} />

);

const Bar = () => (

Bar

);

const Home = () => (

Home

);

const NotFound = () => (

Not Found

);

const routes = {
  '/': Home,
  '/foo': Foo,
  '/bar': Bar
};
// Add routes
Object.keys(routes).forEach(route => store.dispatch(actions.addRoute(route)));

const App = React.createClass({
  getInitialState() {
    return {routingState: store.getState().componentRouter};
  },

  shouldComponentUpdate,

  componentDidMount() {
    this.unsubscribe = store.subscribe(() =>
      this.setState({routingState: store.getState().componentRouter}));
  },

  componentWillUnmount() {
    this.unsubscribe();
  },

  render() {
    const {routingState} = this.state;
    const CurrentComponent = routes[routingState.currentRoute.route] || NotFound;

    return (

component-router

        <CurrentComponent routingState={routingState} />

          Routing state:

{JSON.stringify(routingState, null, 2)}

    );
  }
});

export default App;
<commit_msg>separate example for `off-record` navigation<commit_after>import React from 'react';
import {shouldComponentUpdate} from 'react/lib/ReactComponentWithPureRenderMixin';
import {locationHistory as location, actions, href, isActive} from '../..';
import {createStore} from './store';
import css from './App.css';

const store = createStore();

location({store, namespace: 'componentRouter'});

const navigateTo = params => event => {
  event.preventDefault();
  store.dispatch(actions.navigateTo(params));
};

const GlobalLinks = React.createClass({
  propTypes: {
    routingState: React.PropTypes.object
  },

  shouldComponentUpdate,

  render() {
    const {routingState} = this.props;

    return (

          Home

          /foo

          /bar

          /cleanHistory

          /404

    );
  }
});

const ComponentLinks = React.createClass({
  propTypes: {
    routingState: React.PropTypes.object
  },

  componentDidMount() {
    store.dispatch(actions.addDefaultParam('component', 'baz'));
  },

  shouldComponentUpdate,

  componentWillUnmount() {
    store.dispatch(actions.removeParam('component'));
  },

  render() {
    const {routingState} = this.props;

    return (

        component: bla
        component: baz

    );
  }
});

const componentsList = ['bla', 'baz', 'abc', 'zyx'];
const SortedComponentLinks = React.createClass({
  propTypes: {
    routingState: React.PropTypes.object
  },

  componentDidMount() {
    store.dispatch(actions.addDefaultParam('sortedComponent', 'baz'));
    store.dispatch(actions.addDefaultParam('sorting', 'Unsorted'));
    store.dispatch(actions.addOffRecordParam('sorting'));
  },

  shouldComponentUpdate,

  componentWillUnmount() {
    store.dispatch(actions.removeParam('sortedComponent'));
    store.dispatch(actions.removeParam('sorting'));
  },

  render() {
    const {routingState} = this.props;

    const sortIt = (original, direction) => {
      if (direction === 'Unsorted') return original;

      const sorted = original.slice(0).sort();

      if (direction === 'Descending') {
        sorted.reverse();
      }

      return sorted;
    };

    return (

             No sorting

             Ascending

             Descending

           (sorting changes are not going into the browser history)

        {
          sortIt(componentsList, routingState.query.sorting).map(c => (component: {c}))
        }

    );
  }
});

const Header = ({...props}) => (

      <GlobalLinks {...props} />

);

const Foo = ({...props}) => (

Foo

      <ComponentLinks {...props} />

);

const Bar = () => (

Bar

);

const CleanHistory = ({...props}) => (

CleanHistory

      <SortedComponentLinks {...props} />

);

const Home = () => (

Home

);

const NotFound = () => (

Not Found

);

const routes = {
  '/': Home,
  '/foo': Foo,
  '/bar': Bar,
  '/cleanHistory': CleanHistory
};
// Add routes
Object.keys(routes).forEach(route => store.dispatch(actions.addRoute(route)));

const App = React.createClass({
  getInitialState() {
    return {routingState: store.getState().componentRouter};
  },

  shouldComponentUpdate,

  componentDidMount() {
    this.unsubscribe = store.subscribe(() =>
      this.setState({routingState: store.getState().componentRouter}));
  },

  componentWillUnmount() {
    this.unsubscribe();
  },

  render() {
    const {routingState} = this.state;
    const CurrentComponent = routes[routingState.currentRoute.route] || NotFound;

    return (

component-router

        <CurrentComponent routingState={routingState} />

          Routing state:

{JSON.stringify(routingState, null, 2)}

    );
  }
});

export default App;
<|endoftext|>


<commit_before>import browser from 'bowser'

/**
* Check for contenteditable support
*
* (from Modernizr)
* this is known to false positive in some mobile browsers
* here is a whitelist of verified working browsers:
* https://github.com/NielsLeenheer/html5test/blob/549f6eac866aa861d9649a0707ff2c0157895706/scripts/engine.js#L2083
*/
export const contenteditable = typeof document.documentElement.contentEditable !== 'undefined'

const parser = browser.getParser(window.navigator.userAgent)
const browserName = parser.getBrowser()
const browserEngine = parser.getEngineName()
const webKit = browserEngine === 'WebKit'

/**
 * Check selectionchange event (currently supported in IE, Chrome, Firefox and Safari)
 * Firefox supports it since version 52 (2017) so pretty sure this is fine.
 */
// not exactly feature detection... is it?
export const selectionchange = !(browserName === 'Opera')

// See Keyboard.prototype.preventContenteditableBug for more information.
export const contenteditableSpanBug = !!webKit
<commit_msg>chore(selectionchange): use feature detection<commit_after>import browser from 'bowser'

/**
* Check for contenteditable support
*
* (from Modernizr)
* this is known to false positive in some mobile browsers
* here is a whitelist of verified working browsers:
* https://github.com/NielsLeenheer/html5test/blob/549f6eac866aa861d9649a0707ff2c0157895706/scripts/engine.js#L2083
*/
export const contenteditable = typeof document.documentElement.contentEditable !== 'undefined'

const parser = browser.getParser(window.navigator.userAgent)
const browserEngine = parser.getEngineName()
const webKit = browserEngine === 'WebKit'

/**
 * Check selectionchange event (supported in IE, Chrome, Firefox and Safari)
 * Firefox supports it since version 52 (2017).
 * Opera has no support as of 2021.
 */
const hasNativeSelectionchangeSupport = (document) => {
  const doc = document
  const osc = doc.onselectionchange
  if (osc !== undefined) {
    try {
      doc.onselectionchange = 0
      return doc.onselectionchange === null
    } catch (e) {
    } finally {
      doc.onselectionchange = osc
    }
  }
  return false
}

export const selectionchange = hasNativeSelectionchangeSupport(document)

// See Keyboard.prototype.preventContenteditableBug for more information.
export const contenteditableSpanBug = !!webKit
<|endoftext|>


<commit_before>
var   cmd = require('./process')
    , path = require('path')
    , util = require('./util')
    , fs = require('graceful-fs')

//caches a list of installed haxelibs for the remaining execution,
var libs = {};
var internal = {};

    function HaxelibError(message) {
        this.name = "HaxelibError";
        this.message = message;
    }

    HaxelibError.prototype = Error.prototype;

    exports.init = function init(flow, done) {

            //find haxelib library path
        cmd.exec(flow, 'haxe', ['-version'], { quiet:true }, function(code, out, err) {

            var version = out || err;
            version = version.trim()

            flow.log(2, 'haxe version %s', version);

            var parts = version.split('.');
            flow.haxe = {
                version: version,
                major:parts[0],
                minor:parts[1],
                patch:parts[2]
            }

            exports.init_haxelib(flow, done);

        });

    }

        //caches all libs and values from haxelib list
    exports.init_haxelib = function init_haxelib(flow, done) {

            //always reset these because we are flushing cache
        libs = {};

            //find haxelib library path
        cmd.exec(flow, 'haxelib', ['config'], {quiet:true}, function(code, out, err) {

            if(code != 0) {
                var reason = '> haxelib config cannot be called. Is haxe/haxelib installed?\n';
                throw new HaxelibError(reason);
            }

            exports.haxelib_path = util.normalize(out.trim());

            flow.log(3, 'haxelib path %s', exports.haxelib_path);

                //find and cache the list

            cmd.exec(flow, 'haxelib', ['list'], {quiet:true}, function(lcode, lout, lerr) {

                var list = lout.trim();
                var name_ver = /^([.0-9a-zA-Z-_]*)(:{1})\s(.*)$/igm;

                var match = name_ver.exec(list);
                while (match != null) {
                    var name = match[1];
                    libs[name] = { name:name, _versions_ : match[3] }
                    match = name_ver.exec(list);
                }

                internal.parse_versions(flow);

                if(done) {
                    done();
                }

            });

        });

    } //init

    exports.current = function current(flow, name) {
        if(libs[name]) {
            return libs[name].versions['*'];
        } else {
            return null;
        }
    }

    exports.version = function version(flow, name, ver) {

        if(libs[name]) {
            return libs[name].versions[ver];
        } else {
            return null;
        }

    } //version

    exports.version_list = function version_list(flow, name) {

        var result = [];

        if(libs[name]) {
            var versions = libs[name].versions;
            for(index in versions) {
                if(index != '*') {
                    if(index == 'dev') {
                        result.push(index + ':' + versions[index]);
                    } else {
                        result.push(index);
                    }
                }
            }
        }

        return result.join(', ');

    } //version

    exports.json = function json(flow, name, version) {

            //current if none specified
        version = version || "*";

        flow.log(3, 'haxelib get json for %s(%s)', name, version );

        if(!libs[name]) {
            flow.log(1, 'haxelib - get json for %s(%s) failed, no haxelib under that name', name, version );
            return;
        }

        var lib_path = libs[name].versions[version].path;
        var json_path = path.resolve(lib_path, 'haxelib.json');

        if(fs.existsSync(json_path)) {
            try {
                return require(json_path);
            } catch(e) {
                flow.log(1, 'haxelib.json failed to parse, from %s', json_path);
            }
        }

    } //json

    exports.path = function path(flow, name, version) {

            //current if none specified
        version = version || "*";

        flow.log(3, 'haxelib get path for %s(%s)', name, version );

        return libs[name].versions[version].path;

    } //path

    exports.get = function(flow, name) {

        return libs[name];

    } //get

internal.parse_versions = function(flow) {

    for(lib in libs) {

        var _lib = libs[lib];
        var _split = /([^\s[]+|\[.*\])/igm;
        var _vlist = _lib._versions_.split(_split);

        _lib.versions = {};

        for(index in _vlist) {

            var v = _vlist[index].trim();
            if(!v) continue;
            var current = v.indexOf('[') != -1;

                //strip [ ] if current
            if(current) {
                v = v.replace(/\[(.*)\]/gi,'$1');
            }

            var lib_path = '';
            if(v.substr(0,4) == 'dev:') {
                    //dev path is already here, so store it
                lib_path = v.replace(/dev:/gi,'');
                    //set name to dev
                v = 'dev';
            } else if(v == 'git') {
                    //git folder is in the root like so
                lib_path = path.join( exports.haxelib_path, lib, 'git' );
            } else {
                    //the other types becomes /v,e,r/
                lib_path = path.join( exports.haxelib_path, lib.replace(/\./gi,','), v.replace(/\./gi,',') );
            }

            lib_path = util.normalize(lib_path, true);
            _lib.versions[v] = { version:v, path:lib_path };

            if(current) {
                _lib.versions['*'] = { version:v, path:lib_path };
            }

                //remove _versions_
            delete _lib._versions_;

        } //each in vlist

        flow.log(4, lib, _lib.versions);

    } //each in libs

}
<commit_msg>patch git version as well<commit_after>
var   cmd = require('./process')
    , path = require('path')
    , util = require('./util')
    , fs = require('graceful-fs')

//caches a list of installed haxelibs for the remaining execution,
var libs = {};
var internal = {};

    function HaxelibError(message) {
        this.name = "HaxelibError";
        this.message = message;
    }

    HaxelibError.prototype = Error.prototype;

    exports.init = function init(flow, done) {

            //find haxelib library path
        cmd.exec(flow, 'haxe', ['-version'], { quiet:true }, function(code, out, err) {

            var version = out || err;
            version = version.trim()

            flow.log(2, 'haxe version %s', version);

            var parts = version.split('.');
            flow.haxe = {
                version: version,
                major:parts[0],
                minor:parts[1],
                patch:parts[2]
            }

            exports.init_haxelib(flow, done);

        });

    }

        //caches all libs and values from haxelib list
    exports.init_haxelib = function init_haxelib(flow, done) {

            //always reset these because we are flushing cache
        libs = {};

            //find haxelib library path
        cmd.exec(flow, 'haxelib', ['config'], {quiet:true}, function(code, out, err) {

            if(code != 0) {
                var reason = '> haxelib config cannot be called. Is haxe/haxelib installed?\n';
                throw new HaxelibError(reason);
            }

            exports.haxelib_path = util.normalize(out.trim());

            flow.log(3, 'haxelib path %s', exports.haxelib_path);

                //find and cache the list

            cmd.exec(flow, 'haxelib', ['list'], {quiet:true}, function(lcode, lout, lerr) {

                var list = lout.trim();
                var name_ver = /^([.0-9a-zA-Z-_]*)(:{1})\s(.*)$/igm;

                var match = name_ver.exec(list);
                while (match != null) {
                    var name = match[1];
                    libs[name] = { name:name, _versions_ : match[3] }
                    match = name_ver.exec(list);
                }

                internal.parse_versions(flow);

                if(done) {
                    done();
                }

            });

        });

    } //init

    exports.current = function current(flow, name) {
        if(libs[name]) {
            return libs[name].versions['*'];
        } else {
            return null;
        }
    }

    exports.version = function version(flow, name, ver) {

        if(libs[name]) {
            return libs[name].versions[ver];
        } else {
            return null;
        }

    } //version

    exports.version_list = function version_list(flow, name) {

        var result = [];

        if(libs[name]) {
            var versions = libs[name].versions;
            for(index in versions) {
                if(index != '*') {
                    if(index == 'dev') {
                        result.push(index + ':' + versions[index]);
                    } else {
                        result.push(index);
                    }
                }
            }
        }

        return result.join(', ');

    } //version

    exports.json = function json(flow, name, version) {

            //current if none specified
        version = version || "*";

        flow.log(3, 'haxelib get json for %s(%s)', name, version );

        if(!libs[name]) {
            flow.log(1, 'haxelib - get json for %s(%s) failed, no haxelib under that name', name, version );
            return;
        }

        var lib_path = libs[name].versions[version].path;
        var json_path = path.resolve(lib_path, 'haxelib.json');

        if(fs.existsSync(json_path)) {
            try {
                return require(json_path);
            } catch(e) {
                flow.log(1, 'haxelib.json failed to parse, from %s', json_path);
            }
        }

    } //json

    exports.path = function path(flow, name, version) {

            //current if none specified
        version = version || "*";

        flow.log(3, 'haxelib get path for %s(%s)', name, version );

        return libs[name].versions[version].path;

    } //path

    exports.get = function(flow, name) {

        return libs[name];

    } //get

internal.parse_versions = function(flow) {

    for(lib in libs) {

        var _lib = libs[lib];
        var _split = /([^\s[]+|\[.*\])/igm;
        var _vlist = _lib._versions_.split(_split);

        _lib.versions = {};

        for(index in _vlist) {

            var v = _vlist[index].trim();
            if(!v) continue;
            var current = v.indexOf('[') != -1;

                //strip [ ] if current
            if(current) {
                v = v.replace(/\[(.*)\]/gi,'$1');
            }

            var lib_path = '';
            if(v.substr(0,4) == 'dev:') {
                    //dev path is already here, so store it
                lib_path = v.replace(/dev:/gi,'');
                    //set name to dev
                v = 'dev';
            } else if(v == 'git') {
                    //git folder is in the root like so
                lib_path = path.join( exports.haxelib_path, lib.replace(/\./gi,','), 'git' );
            } else {
                    //the other types becomes /v,e,r/
                lib_path = path.join( exports.haxelib_path, lib.replace(/\./gi,','), v.replace(/\./gi,',') );
            }

            lib_path = util.normalize(lib_path, true);
            _lib.versions[v] = { version:v, path:lib_path };

            if(current) {
                _lib.versions['*'] = { version:v, path:lib_path };
            }

                //remove _versions_
            delete _lib._versions_;

        } //each in vlist

        flow.log(4, lib, _lib.versions);

    } //each in libs

}
<|endoftext|>


<commit_before>// Handler functions for the /assets endpoint.

var
    async = require('async'),
    pkgcloud = require('pkgcloud'),
    fs = require('fs'),
    path = require('path'),
    crypto = require('crypto'),
    config = require('./config'),
    connection = require('./connection'),
    log = require('./logging').getLogger();

/**
 * @description Calculate a checksum of an uploaded file's contents to generate
 *   the fingerprinted asset name.
 */
function fingerprintAsset(asset, callback) {
    var sha256sum = crypto.createHash('sha256');
    var assetFile = fs.createReadStream(asset.path);
    var chunks = [];

    assetFile.on('data', function (chunk) {
        sha256sum.update(chunk);
        chunks.push(chunk);
    });

    assetFile.on('error', callback);

    assetFile.on('end', function() {
        var digest = sha256sum.digest('hex');
        var ext = path.extname(asset.name);
        var basename = path.basename(asset.name, ext);
        var fingerprinted = basename + "-" + digest + ext;

        log.debug({
            originalAssetName: asset.name,
            assetFilename: fingerprinted,
            message: "Asset fingerprinted successfully."
        });

        callback(null, {
            key: asset.key,
            original: asset.name,
            chunks: chunks,
            filename: fingerprinted,
            type: asset.type,
        });
    });
}

/**
 * @description Upload an asset's contents to the asset container.
 */
function publishAsset(asset, callback) {
    var up = connection.client.upload({
        container: config.assetContainer(),
        remote: asset.filename,
        contentType: asset.type,
        headers: { 'Access-Control-Allow-Origin': '*' }
    });

    up.on('error', callback);

    up.on('finish', function () {
        log.debug({
            assetFilename: asset.filename,
            message: "Asset uploaded successfully."
        });

        var baseURI = connection.assetContainer.cdnSslUri;
        asset.publicURL = baseURI + '/' + encodeURIComponent(asset.filename);
        callback(null, asset);
    });

    asset.chunks.forEach(function (chunk) {
        up.write(chunk);
    });

    up.end();
}

/**
 * @description Give this asset a name. The final name and CDL URI of this
 *   asset will be included in all outgoing metadata envelopes, for use by
 *   layouts.
 */
function nameAsset(asset, callback) {
    log.debug({
        originalAssetFilename: asset.original,
        assetName: asset.key,
        message: "Asset named successfully."
    });

    connection.db.collection("layoutAssets").updateOne(
        { key: asset.key },
        { $set: { key: asset.key, publicURL: asset.publicURL } },
        { upsert: true },
        function (err) { callback(err, asset); }
    );
}

/**
 * @description Create and return a function that processes a single asset.
 */
function makeAssetHandler(should_name) {
    return function(asset, callback) {
        log.debug({
            originalAssetName: asset.name,
            message: "Asset upload request received."
        });

        var steps = [
            async.apply(fingerprintAsset, asset),
            publishAsset
        ];

        if (should_name) {
            steps.push(nameAsset);
        }

        async.waterfall(steps, callback);
    };
}

/**
 * @description Fingerprint and upload static, binary assets to the
 *   CDN-enabled ASSET_CONTAINER. Return a JSON object containing a
 *   map of the provided filenames to their final, public URLs.
 */
exports.accept = function (req, res, next) {
    var assetData = Object.getOwnPropertyNames(req.files).map(function (key) {
        var asset = req.files[key];
        asset.key = key;
        return asset;
    });

    log.debug({
        apikeyName: req.apikeyName,
        assetCount: assetData.length,
        message: "Asset upload request received."
    });

    var reqStart = Date.now();

    async.map(assetData, makeAssetHandler(req.query.named), function (err, results) {
        if (err) {
            log.error({
                apikeyName: req.apikeyName,
                error: err.message,
                message: "Unable to upload one or more assets."
            });

            res.send(500, {
                apikeyName: req.apikeyName,
                error: "Unable to upload one or more assets."
            });
            next();
        }

        var summary = {};
        results.forEach(function (result) {
            summary[result.original] = result.publicURL;
        });
        log.info({
            apikeyName: req.apikeyName,
            totalReqDuration: Date.now() - reqStart,
            message: "All assets have been uploaded successfully.",
            summary: summary
        });

        res.send(summary);
        next();
    });
};

exports.list = function (req, res, next) {
    log.debug("Asset list requested.");

    connection.db.collection("layoutAssets").find().toArray(function (err, assetVars) {
        if (err) {
            log.error({
                message: "Unable to list assets.",
                error: err.message
            });
            return next(err);
        }

        var assets = {};

        for (i = 0; i < assetVars.length; i++) {
            var assetVar = assetVars[i];
            assets[assetVar.key] = assetVar.publicURL;
        }

        res.send(assets);
        next();
    });
};
<commit_msg>Listen for the right event from the CF upload!<commit_after>// Handler functions for the /assets endpoint.

var
    async = require('async'),
    pkgcloud = require('pkgcloud'),
    fs = require('fs'),
    path = require('path'),
    crypto = require('crypto'),
    config = require('./config'),
    connection = require('./connection'),
    log = require('./logging').getLogger();

/**
 * @description Calculate a checksum of an uploaded file's contents to generate
 *   the fingerprinted asset name.
 */
function fingerprintAsset(asset, callback) {
    var sha256sum = crypto.createHash('sha256');
    var assetFile = fs.createReadStream(asset.path);
    var chunks = [];

    assetFile.on('data', function (chunk) {
        sha256sum.update(chunk);
        chunks.push(chunk);
    });

    assetFile.on('error', callback);

    assetFile.on('end', function() {
        var digest = sha256sum.digest('hex');
        var ext = path.extname(asset.name);
        var basename = path.basename(asset.name, ext);
        var fingerprinted = basename + "-" + digest + ext;

        log.debug({
            originalAssetName: asset.name,
            assetFilename: fingerprinted,
            message: "Asset fingerprinted successfully."
        });

        callback(null, {
            key: asset.key,
            original: asset.name,
            chunks: chunks,
            filename: fingerprinted,
            type: asset.type,
        });
    });
}

/**
 * @description Upload an asset's contents to the asset container.
 */
function publishAsset(asset, callback) {
    var up = connection.client.upload({
        container: config.assetContainer(),
        remote: asset.filename,
        contentType: asset.type,
        headers: { 'Access-Control-Allow-Origin': '*' }
    });

    up.on('error', callback);

    up.on('success', function () {
        log.debug({
            assetFilename: asset.filename,
            message: "Asset uploaded successfully."
        });

        var baseURI = connection.assetContainer.cdnSslUri;
        asset.publicURL = baseURI + '/' + encodeURIComponent(asset.filename);
        callback(null, asset);
    });

    asset.chunks.forEach(function (chunk) {
        up.write(chunk);
    });

    up.end();
}

/**
 * @description Give this asset a name. The final name and CDL URI of this
 *   asset will be included in all outgoing metadata envelopes, for use by
 *   layouts.
 */
function nameAsset(asset, callback) {
    log.debug({
        originalAssetFilename: asset.original,
        assetName: asset.key,
        message: "Asset named successfully."
    });

    connection.db.collection("layoutAssets").updateOne(
        { key: asset.key },
        { $set: { key: asset.key, publicURL: asset.publicURL } },
        { upsert: true },
        function (err) { callback(err, asset); }
    );
}

/**
 * @description Create and return a function that processes a single asset.
 */
function makeAssetHandler(should_name) {
    return function(asset, callback) {
        log.debug({
            originalAssetName: asset.name,
            message: "Asset upload request received."
        });

        var steps = [
            async.apply(fingerprintAsset, asset),
            publishAsset
        ];

        if (should_name) {
            steps.push(nameAsset);
        }

        async.waterfall(steps, callback);
    };
}

/**
 * @description Fingerprint and upload static, binary assets to the
 *   CDN-enabled ASSET_CONTAINER. Return a JSON object containing a
 *   map of the provided filenames to their final, public URLs.
 */
exports.accept = function (req, res, next) {
    var assetData = Object.getOwnPropertyNames(req.files).map(function (key) {
        var asset = req.files[key];
        asset.key = key;
        return asset;
    });

    log.debug({
        apikeyName: req.apikeyName,
        assetCount: assetData.length,
        message: "Asset upload request received."
    });

    var reqStart = Date.now();

    async.map(assetData, makeAssetHandler(req.query.named), function (err, results) {
        if (err) {
            log.error({
                apikeyName: req.apikeyName,
                error: err.message,
                message: "Unable to upload one or more assets."
            });

            res.send(500, {
                apikeyName: req.apikeyName,
                error: "Unable to upload one or more assets."
            });
            next();
        }

        var summary = {};
        results.forEach(function (result) {
            summary[result.original] = result.publicURL;
        });
        log.info({
            apikeyName: req.apikeyName,
            totalReqDuration: Date.now() - reqStart,
            message: "All assets have been uploaded successfully.",
            summary: summary
        });

        res.send(summary);
        next();
    });
};

exports.list = function (req, res, next) {
    log.debug("Asset list requested.");

    connection.db.collection("layoutAssets").find().toArray(function (err, assetVars) {
        if (err) {
            log.error({
                message: "Unable to list assets.",
                error: err.message
            });
            return next(err);
        }

        var assets = {};

        for (i = 0; i < assetVars.length; i++) {
            var assetVar = assetVars[i];
            assets[assetVar.key] = assetVar.publicURL;
        }

        res.send(assets);
        next();
    });
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'ProxyDAO',
  extends: 'foam.dao.AbstractDAO',
  requires: [
    'foam.dao.ProxyListener'
  ],

  documentation: 'Proxy implementation for the DAO interface.',

  properties: [
    {
      class: 'Proxy',
      of: 'foam.dao.DAO',
      name: 'delegate',
      forwards: [ 'put', 'remove', 'find', 'select', 'removeAll', 'listen' ]
    },
    {
      name: 'of',
      factory: function() {
        return this.delegate.of;
      }
    }
  ],
  methods: [
    function listen(sink, skip, limit, order, predicate) {
      var listener = this.ProxyListener.create({
        delegate: sink,
        args: [skip, limit, order, predicate]
      });

      listener.onDetach(listener.dao$.follow(this.delegate$));

      return listener;
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'ProxyListener',
  implements: ['foam.dao.Sink'],
  properties: [
    'args',
    'delegate',
    {
      name: 'innerSub',
      postSet: function(_, s) {
        if (s) this.onDetach(s);
      }
    },
    {
      name: 'dao',
      postSet: function(old, nu) {
        this.innerSub && this.innerSub.detach();
        this.innerSub = nu && nu.listen.apply(nu, [this].concat(this.args));
        if ( old ) this.reset();
      }
    }
  ],
  methods: [
    function put(s, obj) {
      this.delegate.put(this, obj);
    },
    function remove(s, obj) {
      this.delegate.remove(this, obj);
    },
    function reset(s) {
      this.delegate.reset(this);
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'ArraySink',
  extends: 'foam.dao.AbstractSink',

  properties: [
    {
      class: 'Array',
      name: 'a'
    }
  ],

  methods: [
    function put(sub, o) {
      this.a.push(o);
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'PromisedDAO',
  implements: ['foam.dao.DAO'],

  properties: [
    {
      class: 'Promised',
      of: 'foam.dao.DAO',
      methods: [ 'put', 'remove', 'find', 'select', 'removeAll', 'listen' ],
      name: 'promise'
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'LocalStorageDAO',
  extends: 'foam.dao.ArrayDAO',

  properties: [
    {
      name:  'name',
      label: 'Store Name',
      class:  'foam.core.String'
    }
  ],

  methods: [
    function init() {
      var objs = localStorage.getItem(this.name);
      if ( objs ) this.array = foam.json.parseString(objs, this);

      this.on.put.sub(this.updated);
      this.on.remove.sub(this.updated);

      // TODO: base on an indexed DAO
    }
  ],

  listeners: [
    {
      name: 'updated',
      isMerged: true,
      mergeDelay: 100,
      code: function() {
        localStorage.setItem(this.name, foam.json.stringify(this.array));
      }
    }
  ]
});

foam.LIB({
  name: 'foam.String',
  methods: [
    {
      name: 'daoize',
      code: foam.Function.memoize1(function(str) {
        // Turns SomeClassName into someClassNameDAO,
        // of package.ClassName into package.ClassNameDAO
        return str.substring(0, 1).toLowerCase() + str.substring(1) + 'DAO';
      })
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'InvalidArgumentException',
  extends: 'foam.dao.ExternalException',
  properties: [
    {
      class: 'String',
      name: 'message'
    }
  ]
});
<commit_msg>Give the ProxyDAO delegate a default of NullDAO.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'ProxyDAO',
  extends: 'foam.dao.AbstractDAO',
  requires: [
    'foam.dao.ProxyListener'
  ],

  documentation: 'Proxy implementation for the DAO interface.',

  properties: [
    {
      class: 'Proxy',
      of: 'foam.dao.DAO',
      name: 'delegate',
      forwards: [ 'put', 'remove', 'find', 'select', 'removeAll', 'listen' ],
      factory: function() { return foam.dao.NullDAO.create() },
    },
    {
      name: 'of',
      factory: function() {
        return this.delegate.of;
      }
    }
  ],
  methods: [
    function listen(sink, skip, limit, order, predicate) {
      var listener = this.ProxyListener.create({
        delegate: sink,
        args: [skip, limit, order, predicate]
      });

      listener.onDetach(listener.dao$.follow(this.delegate$));

      return listener;
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'ProxyListener',
  implements: ['foam.dao.Sink'],
  properties: [
    'args',
    'delegate',
    {
      name: 'innerSub',
      postSet: function(_, s) {
        if (s) this.onDetach(s);
      }
    },
    {
      name: 'dao',
      postSet: function(old, nu) {
        this.innerSub && this.innerSub.detach();
        this.innerSub = nu && nu.listen.apply(nu, [this].concat(this.args));
        if ( old ) this.reset();
      }
    }
  ],
  methods: [
    function put(s, obj) {
      this.delegate.put(this, obj);
    },
    function remove(s, obj) {
      this.delegate.remove(this, obj);
    },
    function reset(s) {
      this.delegate.reset(this);
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'ArraySink',
  extends: 'foam.dao.AbstractSink',

  properties: [
    {
      class: 'Array',
      name: 'a'
    }
  ],

  methods: [
    function put(sub, o) {
      this.a.push(o);
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'PromisedDAO',
  implements: ['foam.dao.DAO'],

  properties: [
    {
      class: 'Promised',
      of: 'foam.dao.DAO',
      methods: [ 'put', 'remove', 'find', 'select', 'removeAll', 'listen' ],
      name: 'promise'
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'LocalStorageDAO',
  extends: 'foam.dao.ArrayDAO',

  properties: [
    {
      name:  'name',
      label: 'Store Name',
      class:  'foam.core.String'
    }
  ],

  methods: [
    function init() {
      var objs = localStorage.getItem(this.name);
      if ( objs ) this.array = foam.json.parseString(objs, this);

      this.on.put.sub(this.updated);
      this.on.remove.sub(this.updated);

      // TODO: base on an indexed DAO
    }
  ],

  listeners: [
    {
      name: 'updated',
      isMerged: true,
      mergeDelay: 100,
      code: function() {
        localStorage.setItem(this.name, foam.json.stringify(this.array));
      }
    }
  ]
});

foam.LIB({
  name: 'foam.String',
  methods: [
    {
      name: 'daoize',
      code: foam.Function.memoize1(function(str) {
        // Turns SomeClassName into someClassNameDAO,
        // of package.ClassName into package.ClassNameDAO
        return str.substring(0, 1).toLowerCase() + str.substring(1) + 'DAO';
      })
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'InvalidArgumentException',
  extends: 'foam.dao.ExternalException',
  properties: [
    {
      class: 'String',
      name: 'message'
    }
  ]
});
<|endoftext|>


<commit_before> /**
  * BananaJs
  * Get an RGB color palette from a set of keywords
  *
  * (c) 2015-2020 Alejandro U. Alvarez
  * Bananajs may be freely distributed under the MIT license.
  * For all details and documentation:
  * http://github.com/aurbano/bananajs
  *
  * @author Alejandro U. Alvarez <urbanoalvarez.es>
  * @license MIT License
  */
 "use strict";

 var Bananajs = function(options) {

     var Banana = this;
     Banana.thief = new ColorThief();

     if (typeof(options.googleApi) === "undefined") {
         throw {
             message: "Error: NamedColors: The Google API was not passed, please pass {googleApi: google}"
         };
     }

     var log = function() {
         if (options.debug) console.log.apply(console, arguments);
     };

     options.googleApi.load("search", "1");

     function getImageColor(url, number) {
         return new Promise(function(resolve) {
             var img = new Image();
             img.crossOrigin = "";
             img.onload = function() {
                 try {
                     resolve({
                         color: Banana.thief.getColor(img, options.quality || 10),
                         palette: Banana.thief.getPalette(img, number)
                     });
                 } catch (e) {
                     log("Bananajs: Image download failed");
                     resolve(null);
                 }
             };
             img.onerror = function() {
                 resolve(null);
             };
             img.src = url;
         });
     }

     function getRGBData(colorName, number) {
         return new Promise(function(resolve, reject) {
             // Create an Image Search instance.
             var imageSearch = new options.googleApi.search.ImageSearch();

             // Set searchComplete as the callback function when a search is
             // complete.  The imageSearch object will have results in it.
             imageSearch.setSearchCompleteCallback(this, function() {
                 var images = imageSearch.results;
                 if (images.length < 1) {
                     log("Bananajs: No images found");
                     reject({
                         message: "No results"
                     });
                 }

                 var promises = [];

                 images.forEach(function(image) {
                     log("Bananajs: Loading image: ", image.url);
                     promises.push(getImageColor(image.url, number));
                 });

                 Promise.all(promises).then(function(data) {
                     resolve(data);
                 });
             }, null);

             // Find me a beautiful car.
             imageSearch.execute(colorName);
         });
     }

     function getColor(colorName, resolve) {
         getRGBData(colorName, 0).then(function(data) {
             var r = 0,
                 g = 0,
                 b = 0,
                 count = 0;
             data.forEach(function(info) {
                 if (info !== null) {
                     count++;
                     r += info.color[0];
                     g += info.color[1];
                     b += info.color[2];
                 }
             });
             r /= count;
             g /= count;
             b /= count;
             r = Math.round(r);
             g = Math.round(g);
             b = Math.round(b);

             log("Bananajs: Finished processing, color: ", r, g, b);

             resolve([r, g, b]);
         });
     }

     function getPalette(colorName, number, resolve) {
         getRGBData(colorName, number).then(function(data) {
             var palette = [],
                 count = 0,
                 i;
             for (i = 0; i < number; i++) {
                 palette[i] = [0,0,0];
             }
             data.forEach(function(info) {
                 if (info !== null && info.palette.length > 0) {
                     count++;
                     for (i = 0; i < info.palette.length; i++) {
                         palette[i][0] += info.palette[i][0];
                         palette[i][1] += info.palette[i][1];
                         palette[i][2] += info.palette[i][2];
                     }
                 }
             });
             for (i = 0; i < palette.length; i++) {
                 palette[i][0] = Math.round(palette[i][0] / count);
                 palette[i][1] = Math.round(palette[i][1] / count);
                 palette[i][2] = Math.round(palette[i][2] / count);
             }

             log("Bananajs: Finished processing, palette: ", palette);

             resolve(palette);
         });
     }

     // Expose API
     Banana.getColor = function(colorName) {
         log("Bananajs: Processing color for '" + colorName + "'");
         return new Promise(
             // The resolver function is called with the ability to resolve or
             // reject the promise
             function(resolve, reject) {
                 getColor(colorName, resolve, reject);
             }
         );
     };

     Banana.getPalette = function(colorName, number) {
         log("Bananajs: Processing palette for '" + colorName + "'");
         return new Promise(
             // The resolver function is called with the ability to resolve or
             // reject the promise
             function(resolve, reject) {
                 getPalette(colorName, number, resolve, reject);
             }
         );
     };
 };
<commit_msg>detect if colorthief is missing<commit_after> /**
  * BananaJs
  * Get an RGB color palette from a set of keywords
  *
  * (c) 2015-2020 Alejandro U. Alvarez
  * Bananajs may be freely distributed under the MIT license.
  * For all details and documentation:
  * http://github.com/aurbano/bananajs
  *
  * @author Alejandro U. Alvarez <urbanoalvarez.es>
  * @license MIT License
  */
 "use strict";

 var Bananajs = function(options) {

     var Banana = this;

     if (typeof(options.googleApi) === "undefined") {
         throw {
             message: "Error: Bananajs: The Google API was not passed, please pass {googleApi: google}"
         };
     }

     if (typeof(ColorThief) === "undefined") {
         throw {
             message: "Error: Bananajs: Please include ColorThief"
         };
     }

     Banana.thief = new ColorThief();

     var log = function() {
         if (options.debug) console.log.apply(console, arguments);
     };

     options.googleApi.load("search", "1");

     function getImageColor(url, number) {
         return new Promise(function(resolve) {
             var img = new Image();
             img.crossOrigin = "";
             img.onload = function() {
                 try {
                     resolve({
                         color: Banana.thief.getColor(img, options.quality || 10),
                         palette: Banana.thief.getPalette(img, number)
                     });
                 } catch (e) {
                     log("Bananajs: Image download failed");
                     resolve(null);
                 }
             };
             img.onerror = function() {
                 resolve(null);
             };
             img.src = url;
         });
     }

     function getRGBData(colorName, number) {
         return new Promise(function(resolve, reject) {
             // Create an Image Search instance.
             var imageSearch = new options.googleApi.search.ImageSearch();

             // Set searchComplete as the callback function when a search is
             // complete.  The imageSearch object will have results in it.
             imageSearch.setSearchCompleteCallback(this, function() {
                 var images = imageSearch.results;
                 if (images.length < 1) {
                     log("Bananajs: No images found");
                     reject({
                         message: "No results"
                     });
                 }

                 var promises = [];

                 images.forEach(function(image) {
                     log("Bananajs: Loading image: ", image.url);
                     promises.push(getImageColor(image.url, number));
                 });

                 Promise.all(promises).then(function(data) {
                     resolve(data);
                 });
             }, null);

             // Find me a beautiful car.
             imageSearch.execute(colorName);
         });
     }

     function getColor(colorName, resolve) {
         getRGBData(colorName, 0).then(function(data) {
             var r = 0,
                 g = 0,
                 b = 0,
                 count = 0;
             data.forEach(function(info) {
                 if (info !== null) {
                     count++;
                     r += info.color[0];
                     g += info.color[1];
                     b += info.color[2];
                 }
             });
             r /= count;
             g /= count;
             b /= count;
             r = Math.round(r);
             g = Math.round(g);
             b = Math.round(b);

             log("Bananajs: Finished processing, color: ", r, g, b);

             resolve([r, g, b]);
         });
     }

     function getPalette(colorName, number, resolve) {
         getRGBData(colorName, number).then(function(data) {
             var palette = [],
                 count = 0,
                 i;
             for (i = 0; i < number; i++) {
                 palette[i] = [0,0,0];
             }
             data.forEach(function(info) {
                 if (info !== null && info.palette.length > 0) {
                     count++;
                     for (i = 0; i < info.palette.length; i++) {
                         palette[i][0] += info.palette[i][0];
                         palette[i][1] += info.palette[i][1];
                         palette[i][2] += info.palette[i][2];
                     }
                 }
             });
             for (i = 0; i < palette.length; i++) {
                 palette[i][0] = Math.round(palette[i][0] / count);
                 palette[i][1] = Math.round(palette[i][1] / count);
                 palette[i][2] = Math.round(palette[i][2] / count);
             }

             log("Bananajs: Finished processing, palette: ", palette);

             resolve(palette);
         });
     }

     // Expose API
     Banana.getColor = function(colorName) {
         log("Bananajs: Processing color for '" + colorName + "'");
         return new Promise(function(resolve, reject) {
                 getColor(colorName, resolve, reject);
             }
         );
     };

     Banana.getPalette = function(colorName, number) {
         log("Bananajs: Processing palette for '" + colorName + "'");
         return new Promise(function(resolve, reject) {
                 getPalette(colorName, number, resolve, reject);
             }
         );
     };
 };
<|endoftext|>


<commit_before>(function () {

    'use strict';

    function factory() {

        var ArrayProto = Array.prototype,
            ObjectProto = Object.prototype,
            nativeForEach = ArrayProto.forEach,
            slice = ArrayProto.slice,

            getPrototypeOf = Object.getPrototypeOf,
            createObject = Object.create,

            CLASS_FACTORY_ATTRIBUTE = '*classFactory*';

        function has( object, property ) {
            return object ? ObjectProto.hasOwnProperty.call(object, property) : false;
        }

        function extend( obj ) {
            each(slice.call(arguments, 1), function ( source ) {
                if ( source ) {
                    for ( var prop in source ) {
                        obj[prop] = source[prop];
                    }
                }
            });
            return obj;
        }

        function isObject( obj ) {
            return obj === Object(obj);
        }

        function isUndefined( value ) {
            return typeof value == 'undefined';
        }

        // This each implementation is based on the source code of underscore.js
        // It doesn't comply with the specification at http://es5.github.com/#x15.4.4.18 but is enough for the purposes
        // of this library.
        function each( obj, func, context ) {

            if ( obj === null ) {
                return;
            }

            if ( nativeForEach && obj.forEach === nativeForEach ) {
                obj.forEach(func, context);
            } else if ( obj.length === +obj.length ) {
                for ( var i = 0, l = obj.length; i < l; i++ ) {
                    func.call(context, obj[i], i, obj);
                }
            } else {
                for ( var key in obj ) {
                    if ( has(obj, key) ) {
                        func.call(context, obj[key], key, obj);
                    }
                }
            }

        }

        function mapProperties( srcObj, iterator, result ) {

            if ( !result ) { result = {}; }

            if ( srcObj ) {
                each(srcObj, function ( value, prop ) {

                    result[prop] = iterator.call(this, value, prop);

                }, result);
            }

            return result;

        }

        function assertDefinedProperty( property, name ) {

            if ( isUndefined(property) ) {
                throw new ReferenceError('The property ' + name + ' is not defined');
            }

        }

        function conflict() {

            throw new Error(
                'This property was defined by multiple merged objects, override it with the proper implementation');

        }

        function required() {

            throw new Error('An implementation is required');

        }

        function mergeProperty( value, prop ) {

            /*jshint validthis:true */

            var thisValue = this[prop];

            if ( isUndefined(thisValue) || thisValue === value || thisValue === required ) {
                return value;
            } else if ( value === required ) {
                return thisValue;
            } else {
                return conflict;
            }

        }

        function merge() {

            var result = {};

            each(arguments, function ( obj ) {

                mapProperties(obj, mergeProperty, result);

            });

            return result;

        }

        function Nil() { }

        Nil.__super__ = Nil.prototype;

        Nil.prototype._super = function ( methodName ) {

            var thisPrototype = getPrototypeOf(this),
                superPrototype = getPrototypeOf(thisPrototype),
                self = this;

            if ( !methodName ) {
                return superPrototype;
            } else {
                var superProp = superPrototype[methodName];
                assertDefinedProperty(superProp, methodName);

                if ( typeof superProp == 'function' ) {
                    return function () {
                        return superProp.apply(self, arguments);
                    };
                }

                return superProp;
            }

        };

        function markAsClassFactory( obj ) {

            obj[CLASS_FACTORY_ATTRIBUTE] = true;
            return obj;

        }

        function isClassFactory( obj ) {

            return isObject(obj) && obj[CLASS_FACTORY_ATTRIBUTE] === true;

        }

        var defaultClassFactory = markAsClassFactory({

            createClass: function ( Parent, instanceMethods, staticMethods ) {

                var proto = extend(createObject(Parent.prototype), instanceMethods);

                if ( !has(proto, 'constructor') ) {

                    proto.constructor = function () {
                        Parent.apply(this, arguments);
                    };

                } else if ( typeof proto.constructor !== 'function' ) {

                    throw new TypeError('The constructor property must be a function');

                }

                var ctor = extend(proto.constructor, staticMethods, {__super__: Parent.prototype });

                if ( isUndefined(proto._super) ) { proto._super = Nil.prototype._super; }

                ctor.prototype = proto;
                ctor.extend = Nil.extend;

                return ctor;

            }

        });

        Nil.extend = function () {

            var args = slice.call(arguments),
                classFactory = (isClassFactory(args[0])) ? args.shift() : defaultClassFactory;

            args.unshift(this);

            return classFactory.createClass.apply(classFactory, args);

        };

        var Class = {
            create: function () {

                return Nil.extend.apply(Nil, arguments);

            }
        };

        var AbstractClassFactory = Class.create({

            defaultCreateClass: function () {
                return defaultClassFactory.createClass.apply(defaultClassFactory, arguments);
            },

            createClass: required

        });
        markAsClassFactory(AbstractClassFactory.prototype);

        var TraitsClassFactory = AbstractClassFactory.extend({

            constructor: function ( traits ) { this.traits = traits; },

            createClass: function ( Parent, instanceMethods, staticMethods ) {

                var traitComposition = merge.apply(null, this.traits);

                return this.defaultCreateClass(Parent, extend(traitComposition, instanceMethods), staticMethods);

            }

        });

        function withTraits() {

            return new TraitsClassFactory(slice.call(arguments));

        }

        return {
            extend: extend,
            merge: merge,
            conflict: conflict,

            Nil: Nil,

            markAsClassFactory: markAsClassFactory,
            isClassFactory: isClassFactory,
            AbstractClassFactory: AbstractClassFactory,

            defaultClassFactory: defaultClassFactory,
            Class: Class,

            required: required,

            withTraits: withTraits
        };

    }

    if ( typeof define === 'function' && define.amd ) {
        // AMD (ie requirejs)
        define([], factory);
    } else if ( typeof module !== 'undefined' && module.exports ) {
        // Node
        module.exports = factory();
    } else {
        window.barman = factory();
    }

})();<commit_msg>Small code formatting<commit_after>(function () {

    'use strict';

    function factory() {

        var ArrayProto = Array.prototype,
            ObjectProto = Object.prototype,
            nativeForEach = ArrayProto.forEach,
            slice = ArrayProto.slice,

            getPrototypeOf = Object.getPrototypeOf,
            createObject = Object.create,

            CLASS_FACTORY_ATTRIBUTE = '*classFactory*';

        function has( object, property ) {
            return object ? ObjectProto.hasOwnProperty.call(object, property) : false;
        }

        function extend( obj ) {
            each(slice.call(arguments, 1), function ( source ) {
                if ( source ) {
                    for ( var prop in source ) {
                        obj[prop] = source[prop];
                    }
                }
            });
            return obj;
        }

        function isObject( obj ) {
            return obj === Object(obj);
        }

        function isUndefined( value ) {
            return typeof value == 'undefined';
        }

        // This each implementation is based on the source code of underscore.js
        // It doesn't comply with the specification at http://es5.github.com/#x15.4.4.18 but is enough for the purposes
        // of this library.
        function each( obj, func, context ) {

            if ( obj === null ) {
                return;
            }

            if ( nativeForEach && obj.forEach === nativeForEach ) {
                obj.forEach(func, context);
            } else if ( obj.length === +obj.length ) {
                for ( var i = 0, l = obj.length; i < l; i++ ) {
                    func.call(context, obj[i], i, obj);
                }
            } else {
                for ( var key in obj ) {
                    if ( has(obj, key) ) {
                        func.call(context, obj[key], key, obj);
                    }
                }
            }

        }

        function mapProperties( srcObj, iterator, result ) {

            if ( !result ) { result = {}; }

            if ( srcObj ) {
                each(srcObj, function ( value, prop ) {

                    result[prop] = iterator.call(this, value, prop);

                }, result);
            }

            return result;

        }

        function assertDefinedProperty( property, name ) {

            if ( isUndefined(property) ) {
                throw new ReferenceError('The property ' + name + ' is not defined');
            }

        }

        function conflict() {

            throw new Error(
                'This property was defined by multiple merged objects, override it with the proper implementation');

        }

        function required() {

            throw new Error('An implementation is required');

        }

        function mergeProperty( value, prop ) {

            /*jshint validthis:true */

            var thisValue = this[prop];

            if ( isUndefined(thisValue) || thisValue === value || thisValue === required ) {
                return value;
            } else if ( value === required ) {
                return thisValue;
            } else {
                return conflict;
            }

        }

        function merge() {

            var result = {};

            each(arguments, function ( obj ) {

                mapProperties(obj, mergeProperty, result);

            });

            return result;

        }

        function Nil() { }

        Nil.__super__ = Nil.prototype;

        Nil.prototype._super = function ( methodName ) {

            var thisPrototype = getPrototypeOf(this),
                superPrototype = getPrototypeOf(thisPrototype),
                self = this;

            if ( !methodName ) {
                return superPrototype;
            } else {
                var superProp = superPrototype[methodName];
                assertDefinedProperty(superProp, methodName);

                if ( typeof superProp == 'function' ) {
                    return function () {
                        return superProp.apply(self, arguments);
                    };
                }

                return superProp;
            }

        };

        function markAsClassFactory( obj ) {

            obj[CLASS_FACTORY_ATTRIBUTE] = true;
            return obj;

        }

        function isClassFactory( obj ) {

            return isObject(obj) && obj[CLASS_FACTORY_ATTRIBUTE] === true;

        }

        var defaultClassFactory = markAsClassFactory({

            createClass: function ( Parent, instanceMethods, staticMethods ) {

                var proto = extend(createObject(Parent.prototype), instanceMethods);

                if ( !has(proto, 'constructor') ) {

                    proto.constructor = function () {
                        Parent.apply(this, arguments);
                    };

                } else if ( typeof proto.constructor !== 'function' ) {

                    throw new TypeError('The constructor property must be a function');

                }

                var ctor = extend(proto.constructor, staticMethods, {__super__: Parent.prototype });

                if ( isUndefined(proto._super) ) { proto._super = Nil.prototype._super; }

                ctor.prototype = proto;
                ctor.extend = Nil.extend;

                return ctor;

            }

        });

        Nil.extend = function () {

            var args = slice.call(arguments),
                classFactory = (isClassFactory(args[0])) ? args.shift() : defaultClassFactory;

            args.unshift(this);

            return classFactory.createClass.apply(classFactory, args);

        };

        var Class = {
            create: function () {

                return Nil.extend.apply(Nil, arguments);

            }
        };

        var AbstractClassFactory = Class.create({

            defaultCreateClass: function () {
                return defaultClassFactory.createClass.apply(defaultClassFactory, arguments);
            },

            createClass: required

        });
        markAsClassFactory(AbstractClassFactory.prototype);

        var TraitsClassFactory = AbstractClassFactory.extend({

            constructor: function ( traits ) { this.traits = traits; },

            createClass: function ( Parent, instanceMethods, staticMethods ) {

                var traitComposition = merge.apply(null, this.traits);

                return this.defaultCreateClass(Parent, extend(traitComposition, instanceMethods), staticMethods);

            }

        });

        function withTraits() {

            return new TraitsClassFactory(slice.call(arguments));

        }

        return {
            extend: extend,
            merge: merge,
            conflict: conflict,

            Nil: Nil,

            markAsClassFactory: markAsClassFactory,
            isClassFactory: isClassFactory,
            AbstractClassFactory: AbstractClassFactory,

            defaultClassFactory: defaultClassFactory,
            Class: Class,

            required: required,

            withTraits: withTraits
        };

    }

    if ( typeof define === 'function' && define.amd ) {
        // AMD (ie requirejs)
        define([], factory);
    } else if ( typeof module !== 'undefined' && module.exports ) {
        // Node
        module.exports = factory();
    } else {
        window.barman = factory();
    }

})();<|endoftext|>