<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

'use strict';

const crypto = require('crypto');
const fs = require('fs');
const os = require('os');
const path = require('path');

const {Profiler} = require('graphql-compiler');

/**
 * A file backed cache. Values are JSON encoded on disk, so only JSON
 * serializable values should be used.
 */
class RelayCompilerCache<T> {
  _name: string;
  _cacheBreaker: string;
  _dir: ?string = null;

  /**
   * @param name         Human readable identifier for the cache
   * @param cacheBreaker This should be changed in order to invalidate existing
   *                     caches.
   */
  constructor(name: string, cacheBreaker: string) {
    this._name = name;
    this._cacheBreaker = cacheBreaker;
  }

  _getFile(key: string): string {
    if (this._dir == null) {
      // Include username in the cache dir to avoid issues with directories being
      // owned by a different user.
      const username = os.userInfo().username;
      const cacheID = crypto
        .createHash('md5')
        .update(this._cacheBreaker)
        .update(username)
        .digest('hex');
      const dir = path.join(os.tmpdir(), `${this._name}-${cacheID}`);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
      }
      this._dir = dir;
    }
    return path.join(this._dir, key);
  }

  getOrCompute(key: string, compute: () => T): T {
    return Profiler.run('RelayCompilerCache.getOrCompute', () => {
      const cacheFile = this._getFile(key);
      if (fs.existsSync(cacheFile)) {
        try {
          return JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
        } catch (e) {
          // ignore
        }
      }
      const value = compute();
      try {
        fs.writeFileSync(cacheFile, JSON.stringify(value), 'utf8');
      } catch (e) {
        // ignore
      }
      return value;
    });
  }
}

module.exports = RelayCompilerCache;
<commit_msg>Legacy compiler: fix mkdir in multiple processes<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

'use strict';

const crypto = require('crypto');
const fs = require('fs');
const os = require('os');
const path = require('path');

const {Profiler} = require('graphql-compiler');

/**
 * A file backed cache. Values are JSON encoded on disk, so only JSON
 * serializable values should be used.
 */
class RelayCompilerCache<T> {
  _name: string;
  _cacheBreaker: string;
  _dir: ?string = null;

  /**
   * @param name         Human readable identifier for the cache
   * @param cacheBreaker This should be changed in order to invalidate existing
   *                     caches.
   */
  constructor(name: string, cacheBreaker: string) {
    this._name = name;
    this._cacheBreaker = cacheBreaker;
  }

  _getFile(key: string): string {
    if (this._dir == null) {
      // Include username in the cache dir to avoid issues with directories being
      // owned by a different user.
      const username = os.userInfo().username;
      const cacheID = crypto
        .createHash('md5')
        .update(this._cacheBreaker)
        .update(username)
        .digest('hex');
      const dir = path.join(os.tmpdir(), `${this._name}-${cacheID}`);
      if (!fs.existsSync(dir)) {
        try {
          fs.mkdirSync(dir);
        } catch (error) {
          if (error.code !== 'EEXIST') {
            throw error;
          }
        }
      }
      this._dir = dir;
    }
    return path.join(this._dir, key);
  }

  getOrCompute(key: string, compute: () => T): T {
    return Profiler.run('RelayCompilerCache.getOrCompute', () => {
      const cacheFile = this._getFile(key);
      if (fs.existsSync(cacheFile)) {
        try {
          return JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
        } catch {
          // ignore
        }
      }
      const value = compute();
      try {
        fs.writeFileSync(cacheFile, JSON.stringify(value), 'utf8');
      } catch {
        // ignore
      }
      return value;
    });
  }
}

module.exports = RelayCompilerCache;
<|endoftext|>


<commit_before>var DummyExchange = require('./exchanges/dummy.js').DummyExchange,
    BitmeExchange = require('./exchanges/bitme.js').BitmeExchange,
    BitstampExchange = require('./exchanges/bitstamp.js').BitstampExchange,
    async = require('async'),
    util = require('./util.js'),
    order = require('./order.js'),
    logger = require('./logger.js');

/**
 * Controller which glues all the pieces together.
 *
 * @param {BaseExchange} originExchange Exchange that we're providing liquidity for.
 * @param {BaseExchange} remoteExchange Exchange where we're pulling the orderbook from.
 * @param {number} premium Price multiplier when placing corresponding orders from remote to origin.
 */
var Bot = module.exports.Bot = function(originExchange, remoteExchange, premium) {
    this.state = 'idle';
    this.originExchange = originExchange;
    this.remoteExchange = remoteExchange;
    this.premium = premium;

    this.eventHandlers = {
        'origin': {'trade': this.handleOriginTrade},
        'remote': {'trade': this.handleRemoteTrade, 'orderbook': this.handleRemoteOrderbook}
    };
};

/**
 * Create a new Bot instance based on configuration options.
 *
 * @param {object} config
 * @return {Bot}
 */
Bot.fromConfig = function(config) {
    if (config.live !== true) {
        logger.info('Bot created in dummy mode. All trades will be fake using a DummyExchange.');

        var originExchange = new DummyExchange('DummyOrigin', 1000);
        var remoteExchange = new DummyExchange('DummyRemote', 1000);
        return new Bot(originExchange, remoteExchange);
    }

    if (util.hasStringValues(config, BitstampExchange.configKeys) && util.hasStringValues(config, BitmeExchange.configKeys)) {
        logger.info('Bot created in LIVE mode. Real trades will occur on real exchanges.');
    } else {
        logger.info('Bot created in PRETEND mode due to missing API keys. Orderbook will be watched but no trades will be placed.');
        config.pretend = true;
    }

    var originExchange = BitmeExchange.fromConfig(config);
    var remoteExchange = BitstampExchange.fromConfig(config);
    return new Bot(originExchange, remoteExchange, config.premium);
};

/**
 * Start trading. Will call {@code this.reset()} before commencing.
 *
 * Origin exchange:
 * 1. On 'trade' event, sync orders to remote exchange.
 *
 * Remote exchange:
 * 1. On 'trade' event, log them.
 * 2. On 'orderbook' event, sync orders to origin exchange.
 *
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.start = function(callback) {
    var bot = this;

    async.series([
        function prepareOrigin(callback) { bot.originExchange.ready(callback); },
        function prepareRemote(callback) { bot.remoteExchange.ready(callback); },
        function reset(callback) { bot.reset(callback); },
        function start(callback) {
            logger.debug('[bot] Binding to exchange events.');

            bot.state = 'start';
            util.addListeners(bot.originExchange, bot.eventHandlers.origin);
            util.addListeners(bot.remoteExchange, bot.eventHandlers.remote);

            callback();
        }
    ], function resultHandler(err, result) {
        if (err) bot.abort(err);
        else {
            logger.info('Bot started.');
        }

        callback && callback.call(this, err, result);
    });
};

/**
 * Gracefully shut down the bot by entering into a safe state.
 *
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.stop = function(callback) {
    logger.info('Bot stopping.');

    var bot = this;
    var cleanup = function(callback) {
        logger.debug("[bot] Cleaning up exchange subscriptions.");
        bot.originExchange.cleanup();
        bot.remoteExchange.cleanup();
        util.removeListeners(bot.originExchange, bot.eventHandlers.origin);
        util.removeListeners(bot.remoteExchange, bot.eventHandlers.remote);

        bot.state = 'idle';
        callback && callback.call(bot);
    };

    if (this.state !== 'idle') {
        this.reset(function() {
            cleanup(callback);
        });
    } else {
        cleanup(callback);
    }
};

/**
 * Handle sudden aborts when things go wrong. Similar to stop but takes an error.
 *
 * @param {Error} err Reason for aborting.
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.abort = function(err, callback) {
    logger.error("Aborting bot: ", err);

    // TODO: Do we need extra logic for abort? Alert?

    this.stop(callback);
};

/**
 * Reset the exchanges into a safe state for starting or stopping.
 *
 * This function should be safe to call multiple times without risk of conflict.
 *
 * Origin exchange:
 * 1. Get trades since last state sync and clear orders. (Race condition risk?)
 * 2. Match trades on remote exchange.
 *
 * Remote exchange:
 * 1. Get outstanding orders and log them, send alerts if necessary.
 *
 * Note that outstanding orders on remote exchanges will remain, as these are
 * orders which already had proxy-orders fulfilled in the origin exchange and
 * are supposed to be matched.
 *
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.reset = function(callback) {
    logger.info('Resetting exchanges into a safe state.');

    // XXX: Fill this in.

    callback && callback.call(this);
};

/** Trade event handlers. */

Bot.prototype.handleOriginTrade = function(order, callback) {
    logger.debug('[bot:origin] Trade: %j', order);

    var newOrder = order.clone({}, this.premium, true);
    this.remoteExchange.placeOrders([newOrder], callback);
};

Bot.prototype.handleRemoteTrade = function(order, callback) {
    logger.debug('[bot:remote] Trade: %j', order);
};

Bot.prototype.handleRemoteOrderbook = function(orderbook, callback) {
    logger.debug('[bot:remote] Orderbook: %d asks, %d bids', orderbook.asks.length, orderbook.bids.length);

    var newOrders = order.aggregateOrders(orderbook.asks.concat(orderbook.bids), this.minValue, this.premium);
    var instructions = order.patchOrders(this.originExchange.getOrders(), newOrders);

    // XXX: async.parallel?
    this.originExchange.cancelOrders(instructions.cancel);
    this.originExchange.placeOrders(instructions.place, callback);
};

/**/
<commit_msg>Dummy event handling fix.<commit_after>var DummyExchange = require('./exchanges/dummy.js').DummyExchange,
    BitmeExchange = require('./exchanges/bitme.js').BitmeExchange,
    BitstampExchange = require('./exchanges/bitstamp.js').BitstampExchange,
    async = require('async'),
    util = require('./util.js'),
    order = require('./order.js'),
    logger = require('./logger.js');

/**
 * Controller which glues all the pieces together.
 *
 * @param {BaseExchange} originExchange Exchange that we're providing liquidity for.
 * @param {BaseExchange} remoteExchange Exchange where we're pulling the orderbook from.
 * @param {number} premium Price multiplier when placing corresponding orders from remote to origin.
 */
var Bot = module.exports.Bot = function(originExchange, remoteExchange, premium) {
    this.state = 'idle';
    this.originExchange = originExchange;
    this.remoteExchange = remoteExchange;
    this.premium = premium;

    this.eventHandlers = {
        'origin': {'trade': this.handleOriginTrade.bind(this)},
        'remote': {'trade': this.handleRemoteTrade.bind(this), 'orderbook': this.handleRemoteOrderbook.bind(this)}
    };
};

/**
 * Create a new Bot instance based on configuration options.
 *
 * @param {object} config
 * @return {Bot}
 */
Bot.fromConfig = function(config) {
    if (config.live !== true) {
        logger.info('Bot created in dummy mode. All trades will be fake using a DummyExchange.');

        var originExchange = new DummyExchange('DummyOrigin', 1000);
        var remoteExchange = new DummyExchange('DummyRemote', 1000);
        return new Bot(originExchange, remoteExchange);
    }

    if (util.hasStringValues(config, BitstampExchange.configKeys) && util.hasStringValues(config, BitmeExchange.configKeys)) {
        logger.info('Bot created in LIVE mode. Real trades will occur on real exchanges.');
    } else {
        logger.info('Bot created in PRETEND mode due to missing API keys. Orderbook will be watched but no trades will be placed.');
        config.pretend = true;
    }

    var originExchange = BitmeExchange.fromConfig(config);
    var remoteExchange = BitstampExchange.fromConfig(config);
    return new Bot(originExchange, remoteExchange, config.premium);
};

/**
 * Start trading. Will call {@code this.reset()} before commencing.
 *
 * Origin exchange:
 * 1. On 'trade' event, sync orders to remote exchange.
 *
 * Remote exchange:
 * 1. On 'trade' event, log them.
 * 2. On 'orderbook' event, sync orders to origin exchange.
 *
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.start = function(callback) {
    var bot = this;

    async.series([
        function prepareOrigin(callback) { bot.originExchange.ready(callback); },
        function prepareRemote(callback) { bot.remoteExchange.ready(callback); },
        function reset(callback) { bot.reset(callback); },
        function start(callback) {
            logger.debug('[bot] Binding to exchange events.');

            bot.state = 'start';
            util.addListeners(bot.originExchange, bot.eventHandlers.origin);
            util.addListeners(bot.remoteExchange, bot.eventHandlers.remote);

            callback();
        }
    ], function resultHandler(err, result) {
        if (err) bot.abort(err);
        else {
            logger.info('Bot started.');
        }

        callback && callback.call(this, err, result);
    });
};

/**
 * Gracefully shut down the bot by entering into a safe state.
 *
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.stop = function(callback) {
    logger.info('Bot stopping.');

    var bot = this;
    var cleanup = function(callback) {
        logger.debug("[bot] Cleaning up exchange subscriptions.");
        bot.originExchange.cleanup();
        bot.remoteExchange.cleanup();
        util.removeListeners(bot.originExchange, bot.eventHandlers.origin);
        util.removeListeners(bot.remoteExchange, bot.eventHandlers.remote);

        bot.state = 'idle';
        callback && callback.call(bot);
    };

    if (this.state !== 'idle') {
        this.reset(function() {
            cleanup(callback);
        });
    } else {
        cleanup(callback);
    }
};

/**
 * Handle sudden aborts when things go wrong. Similar to stop but takes an error.
 *
 * @param {Error} err Reason for aborting.
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.abort = function(err, callback) {
    logger.error("Aborting bot: ", err);

    // TODO: Do we need extra logic for abort? Alert?

    this.stop(callback);
};

/**
 * Reset the exchanges into a safe state for starting or stopping.
 *
 * This function should be safe to call multiple times without risk of conflict.
 *
 * Origin exchange:
 * 1. Get trades since last state sync and clear orders. (Race condition risk?)
 * 2. Match trades on remote exchange.
 *
 * Remote exchange:
 * 1. Get outstanding orders and log them, send alerts if necessary.
 *
 * Note that outstanding orders on remote exchanges will remain, as these are
 * orders which already had proxy-orders fulfilled in the origin exchange and
 * are supposed to be matched.
 *
 * @param {function=} callback Function called upon completion.
 */
Bot.prototype.reset = function(callback) {
    logger.info('Resetting exchanges into a safe state.');

    // XXX: Fill this in.

    callback && callback.call(this);
};

/** Trade event handlers. */

Bot.prototype.handleOriginTrade = function(order, callback) {
    logger.debug('[bot:origin] Trade: %j', order);

    var newOrder = order.clone({}, this.premium, true);
    this.remoteExchange.placeOrders([newOrder], callback);
};

Bot.prototype.handleRemoteTrade = function(order, callback) {
    logger.debug('[bot:remote] Trade: %j', order);
};

Bot.prototype.handleRemoteOrderbook = function(orderbook, callback) {
    logger.debug('[bot:remote] Orderbook: %d asks, %d bids', orderbook.asks.length, orderbook.bids.length);

    var newOrders = order.aggregateOrders(orderbook.asks.concat(orderbook.bids), this.minValue, this.premium);
    var instructions = order.patchOrders(this.originExchange.getOrders(), newOrders);

    // XXX: async.parallel?
    this.originExchange.cancelOrders(instructions.cancel);
    this.originExchange.placeOrders(instructions.place, callback);
};

/**/
<|endoftext|>


<commit_before>var inherit = require( 'bloodline' )
var util = require( 'util' )
var tty = require( 'tty' )
var isBuffer = Buffer.isBuffer

const initial = [ 0xFE, 0xFF, 0xFF ]

/**
 * CHS
 * @param {Number} cylinder
 * @param {Number} head
 * @param {Number} sector
 * @return {CHS}
 */
function CHS( cylinder, head, sector ) {

  if( !(this instanceof CHS) )
    return new CHS( cylinder, head, sector )

  Buffer.call( this, 3 )

  this[0] = 0xFE
  this[1] = 0xFF
  this[2] = 0xFF

  // ( buffer, from, to )
  if( isBuffer( cylinder ) ) {
    cylinder.copy( this, 0, head, sector )
  } else {
    this.cylinder = cylinder
    this.head = head
    this.sector = sector
  }

}

/**
 * Set to the CHS of a Logical Block Address
 * @param {Number} lba
 * @param {Number} hpt Heads per Track
 * @param {Number} spt Sectors per Track
 */
CHS.fromLBA = function( lba, hpt, spt ) {
  return new CHS().setLBA( lba, hpt, spt )
}

/**
 * CHS prototype
 * @type {Object}
 */
CHS.prototype = {

  constructor: CHS,

  get head() {
    return this[0]
  },

  // Sector in bits 5–0;
  // bits 7–6 are high bits of cylinder
  // 00111111b
  get sector() {
    return this[1] & 0x3F
  },

  // Bits 7-6 from sector & bits 7–0 of cylinder
  // 11000000b
  get cylinder() {
    return ( ( this[1] & 0xC0 ) << 2 ) | this[2]
  },

  set head( value ) {
    this[0] = value
  },

  set sector( value ) {
    var sector = ( value >> 2 ) & 0xC0
    sector = sector ^ value
    this[1] = sector
  },

  set cylinder( value ) {
    this[2] = value & 0xFF
  },

  /**
   * Set to the CHS of a Logical Block Address
   * @param {Number} lba
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   */
  setLBA: function( lba, hpt, spt ) {
    this.cylinder = ( lba / ( spt * hpt )) | 0
    this.head     = ( lba / spt ) % hpt
    this.sector   = ( lba % spt ) + 1
    return this
  },

  /**
   * Get the Logical Block Address corresponding
   * to the given disk geometry
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   */
  getLBA: function( hpt, spt ) {
    return ( this.cylinder * hpt + this.head ) *
      spt + ( this.sector - 1 )
  },

  /**
   * @see #getLBA()
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   */
  toLBA: function( hpt, spt ) {
    return this.getLBA( hpt, spt )
  },

  toJSON: function() {
    return {
      cylinder: this.cylinder,
      head: this.head,
      sector: this.sector,
    }
  },

  inspect: function() {
    return '<' + this.constructor.name + ' ' +
      util.inspect( this.toJSON(), {
        colors: tty.isatty()
      }) + '>'
  },

}

// Inherit from Buffer
inherit( CHS, Buffer )
// Exports
module.exports = CHS
<commit_msg>Rewrite lib/chs: Remove buffer-inheritance, add methods<commit_after>/**
 * Cylinder-Head-Sector Address
 * @constructor
 * @param {Number} cylinder
 * @param {Number} head
 * @param {Number} sector
 * @return {CHS}
 */
function CHS( cylinder, head, sector ) {

  if( !(this instanceof CHS) )
    return new CHS( cylinder, head, sector )

  if( Buffer.isBuffer( cylinder ) )
    return CHS.fromBuffer( cylinder )

  if( cylinder != null && !Number.isFinite( cylinder ) )
    throw new TypeError( 'Cylinder must be a number' )

  if( head != null && !Number.isFinite( head ) )
    throw new TypeError( 'Head must be a number' )

  if( sector != null && !Number.isFinite( sector ) )
    throw new TypeError( 'Sector must be a number' )

  this.cylinder = cylinder != null ?
    ( cylinder | 0 ) : 1023

  this.head = head != null ?
    ( head | 0 ) : 254

  this.sector = sector != null ?
    ( sector | 0 ) : 63

}

/**
 * Create a CHS Address from a given buffer
 * @param  {Buffer} buffer
 * @return {CHS}
 */
CHS.fromBuffer = function( buffer ) {
  return new CHS().parse( buffer )
}

/**
 * Create a CHS Address from a Logical Block Address (LBA)
 * @param {Number} lba Logical Block Address
 * @param {Number} hpt Heads per Track
 * @param {Number} spt Sectors per Track
 * @return {CHS}
 */
CHS.fromLBA = function( lba, hpt, spt ) {
  return new CHS().setLBA( lba, hpt, spt )
}

/**
 * CHS prototype
 * @type {Object}
 */
CHS.prototype = {

  constructor: CHS,

  /**
   * Get/set values from/to a Buffer
   * @property {Buffer} buffer
   */
  set buffer( value ) { return this.parse( value ) },
  get buffer() { return this.toBuffer() },

  /**
   * Set CHS to a Logical Block Address (LBA)
   * @param {Number} lba Logical Block Address
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {CHS}
   */
  setLBA: function( lba, hpt, spt ) {
    this.cylinder = ( lba / ( spt * hpt )) | 0
    this.head     = ( lba / spt ) % hpt
    this.sector   = ( lba % spt ) + 1
    return this
  },

  /**
   * Get the Logical Block Address (LBA)
   * corresponding to the given disk geometry
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {Number} lba
   */
  getLBA: function( hpt, spt ) {
    return ( this.cylinder * hpt + this.head ) *
      spt + ( this.sector - 1 )
  },

  /**
   * @see #getLBA()
   * @param {Number} hpt Heads per Track
   * @param {Number} spt Sectors per Track
   * @return {Number} lba
   */
  toLBA: function( hpt, spt ) {
    return this.getLBA( hpt, spt )
  },

  /**
   * Parse a given Buffer
   * @param  {Buffer} buffer
   * @return {CHS}
   */
  parse: function( buffer ) {

    if( !Buffer.isBuffer( buffer ) )
      throw new TypeError( 'Value must be a buffer' )

    this.head = buffer[0]

    // Sector in bits 5–0;
    // bits 7–6 are high bits of cylinder
    // 00111111b
    this.sector = buffer[1] & 0x3F

    // Bits 7-6 from sector & bits 7–0 of cylinder
    // 11000000b
    this.cylinder = ( ( buffer[1] & 0xC0 ) << 2 ) | buffer[2]

    return this

  },

  /**
   * Create a Buffer representation of the CHS Address
   * @return {Buffer}
   */
  toBuffer: function() {

    var cylinder = this.cylinder & 0xFF
    var head = this.head
    var sector = (( this.sector >> 2 ) & 0xC0) ^ this.sector

    return new Buffer([ head, sector, cylinder ])

  },

}

// Exports
module.exports = CHS
<|endoftext|>


<commit_before>(function (FMS, Backbone, _, $) {
    _.extend( FMS, {
        LoginView: FMS.FMSView.extend({
            template: 'login',
            id: 'login',
            next: 'around',
            prev: 'around',

            events: {
                'pagehide': 'destroy',
                'pagebeforeshow': 'beforeDisplay',
                'pageshow': 'afterDisplay',
                'vclick #login': 'onClickLogin',
                'submit #signinForm': 'onClickLogin',
                'vclick #logout': 'onClickLogout',
                'vclick .ui-btn-left': 'onClickButtonPrev',
                'vclick .ui-btn-right': 'onClickButtonNext'
            },

            onClickLogin: function(e) {
                // prevent form submission from onscreen keyboard
                e.preventDefault();
                $('#login').focus();
                if ( this.validate() ) {
                    var that = this;
                    $.ajax( {
                        url: CONFIG.FMS_URL + '/auth/ajax/sign_in',
                        type: 'POST',
                        data: {
                            email: $('#form_email').val(),
                            password_sign_in: $('#form_password').val(),
                            remember_me: 1
                        },
                        dataType: 'json',
                        timeout: 30000,
                        success: function( data, status ) {
                            if ( data.name ) {
                                that.model.set('password', $('#form_password').val());
                                that.model.set('email', $('#form_email').val());
                                that.model.set('name', data.name);
                                that.model.save();
                                FMS.isLoggedIn = 1;
                                that.$('#password_row').hide();
                                that.$('#success_row').show();
                            } else {
                                that.validationError('form_email', FMS.strings.login_details_error);
                            }
                        },
                        error: function() {
                            that.validationError('form_email', FMS.strings.login_error);
                        }
                    } );
                }
            },

            onClickLogout: function(e) {
                e.preventDefault();
                var that = this;
                $.ajax( {
                    url: CONFIG.FMS_URL + '/auth/ajax/sign_out',
                    type: 'GET',
                    dataType: 'json',
                    timeout: 30000,
                    success: function( data, status ) {
                        FMS.isLoggedIn = 0;
                        that.model.set('password', '');
                        that.model.save();
                        that.$('#form_email').val('');
                        that.$('#form_password').val('');
                        that.$('#success_row').hide();
                        that.$('#signed_in_row').hide();
                        that.$('#password_row').show();
                    },
                    error: function() {
                        that.validationError('err', FMS.strings.logout_error);
                    }
                } );
            },

            validate: function() {
                this.clearValidationErrors();
                var isValid = 1;

                if ( !$('#form_password').val() ) {
                    isValid = 0;
                    this.validationError('form_password', FMS.validationStrings.password );
                }

                var email = $('#form_email').val();
                if ( !email ) {
                    isValid = 0;
                    this.validationError('form_email', FMS.validationStrings.email.required);
                // regexp stolen from jquery validate module
                } else if ( ! /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(email) ) {
                    isValid = 0;
                    this.validationError('form_email', FMS.validationStrings.email.email);
                }

                if ( !isValid ) {
                    // this makes sure the onscreen keyboard is dismissed
                    $('#login').focus();
                }

                return isValid;
            }
        })
    });
})(FMS, Backbone, _, $);
<commit_msg>do not highlight the email field for general login errors<commit_after>(function (FMS, Backbone, _, $) {
    _.extend( FMS, {
        LoginView: FMS.FMSView.extend({
            template: 'login',
            id: 'login',
            next: 'around',
            prev: 'around',

            events: {
                'pagehide': 'destroy',
                'pagebeforeshow': 'beforeDisplay',
                'pageshow': 'afterDisplay',
                'vclick #login': 'onClickLogin',
                'submit #signinForm': 'onClickLogin',
                'vclick #logout': 'onClickLogout',
                'vclick .ui-btn-left': 'onClickButtonPrev',
                'vclick .ui-btn-right': 'onClickButtonNext'
            },

            onClickLogin: function(e) {
                // prevent form submission from onscreen keyboard
                e.preventDefault();
                $('#login').focus();
                if ( this.validate() ) {
                    var that = this;
                    $.ajax( {
                        url: CONFIG.FMS_URL + '/auth/ajax/sign_in',
                        type: 'POST',
                        data: {
                            email: $('#form_email').val(),
                            password_sign_in: $('#form_password').val(),
                            remember_me: 1
                        },
                        dataType: 'json',
                        timeout: 30000,
                        success: function( data, status ) {
                            if ( data.name ) {
                                that.model.set('password', $('#form_password').val());
                                that.model.set('email', $('#form_email').val());
                                that.model.set('name', data.name);
                                that.model.save();
                                FMS.isLoggedIn = 1;
                                that.$('#password_row').hide();
                                that.$('#success_row').show();
                            } else {
                                that.validationError('signinForm', FMS.strings.login_details_error);
                            }
                        },
                        error: function() {
                            that.validationError('signinForm', FMS.strings.login_error);
                        }
                    } );
                }
            },

            onClickLogout: function(e) {
                e.preventDefault();
                var that = this;
                $.ajax( {
                    url: CONFIG.FMS_URL + '/auth/ajax/sign_out',
                    type: 'GET',
                    dataType: 'json',
                    timeout: 30000,
                    success: function( data, status ) {
                        FMS.isLoggedIn = 0;
                        that.model.set('password', '');
                        that.model.save();
                        that.$('#form_email').val('');
                        that.$('#form_password').val('');
                        that.$('#success_row').hide();
                        that.$('#signed_in_row').hide();
                        that.$('#password_row').show();
                    },
                    error: function() {
                        that.validationError('err', FMS.strings.logout_error);
                    }
                } );
            },

            validate: function() {
                this.clearValidationErrors();
                var isValid = 1;

                if ( !$('#form_password').val() ) {
                    isValid = 0;
                    this.validationError('form_password', FMS.validationStrings.password );
                }

                var email = $('#form_email').val();
                if ( !email ) {
                    isValid = 0;
                    this.validationError('form_email', FMS.validationStrings.email.required);
                // regexp stolen from jquery validate module
                } else if ( ! /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(email) ) {
                    isValid = 0;
                    this.validationError('form_email', FMS.validationStrings.email.email);
                }

                if ( !isValid ) {
                    // this makes sure the onscreen keyboard is dismissed
                    $('#login').focus();
                }

                return isValid;
            }
        })
    });
})(FMS, Backbone, _, $);
<|endoftext|>


<commit_before>import test from 'ava'

var sletTest = require('slettest')
const Slet = require('../../../slet')
const BaseController = require('../..')

test.cb('should respond with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
    .get('/?callback=something')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /something\(\{\"count\":1\}\);/, t.end);
})

test.cb('should use first callback parameter with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
    .get('/?callback=something&callback=somethingelse')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /something\(\{"count":1\}\);/, t.end);
})

test.cb('should ignore object callback parameter with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
    .get('/?callback[a]=something')
    .expect('Content-Type', 'application/json; charset=utf-8')
    .expect(200, '{"count":1}', t.end);
})

test.cb('should allow []', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp2')

  sletTest(app)
    .get('/?callback=callbacks[123]')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /callbacks\[123\]\(\{"count":1\}\);/, t.end);
})

test.cb('should ignore object callback parameter with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp2')

  sletTest(app)
    .get('/2?clb=something')
    // .expect('Content-Type', 'application/json')
    .expect(200, '{"count":1}', t.end);
})

test.cb('should allow renaming callback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp3')

  sletTest(app)
    .get('/3?clb=something')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /something\(\{"count":1\}\);/, t.end);
})

test.cb('should disallow arbitrary js', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp4')

  sletTest(app)
    .get('/4?callback=foo;bar()')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /foobar\(\{\}\);/, t.end);
})

test.cb('should escape utf whitespace', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp5')

  sletTest(app)
  .get('/5?callback=foo')
  .expect('Content-Type', 'text/javascript; charset=utf-8')
  .expect(200, /foo\(\{"str":"\\u2028 \\u2029 woot"\}\);/, t.end);
})

test.cb('should not escape utf whitespace for json fallback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp6')

  sletTest(app)
  .get('/6')
  .expect('Content-Type', 'application/json; charset=utf-8')
  .expect(200, '{"str":"\u2028 \u2029 woot"}', t.end);
})

test.cb('should include security header and prologue', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
  .get('/?callback=something')
  .expect('Content-Type', 'text/javascript; charset=utf-8')
  .expect('X-Content-Type-Options', 'nosniff')
  .expect(200, /^\/\*\*\//, t.end);
})

test.cb('should not override previous Content-Types with no callback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp7')

  sletTest(app)
  .get('/7')
  .expect('Content-Type', 'application/vnd.example+json')
  // .expect(shouldNotHaveHeader('X-Content-Type-Options'))
  .expect(200, '{"hello":"world"}',  t.end);
})

test.cb('should override previous Content-Types with callback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp7')

  sletTest(app)
  .get('/7?callback=cb')
  .expect('Content-Type', 'text/javascript; charset=utf-8')
  .expect('X-Content-Type-Options', 'nosniff')
  .expect(200, /cb\(\{"hello":"world"\}\);$/,  t.end);
})
<commit_msg>jsonp should override previous Content-Types with callback<commit_after>import test from 'ava'

var sletTest = require('slettest')
const Slet = require('../../../slet')
const BaseController = require('../..')

test.cb('should respond with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
    .get('/?callback=something')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /something\(\{\"count\":1\}\);/, t.end);
})

test.cb('should use first callback parameter with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
    .get('/?callback=something&callback=somethingelse')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /something\(\{"count":1\}\);/, t.end);
})

test.cb('should ignore object callback parameter with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
    .get('/?callback[a]=something')
    .expect('Content-Type', 'application/json; charset=utf-8')
    .expect(200, '{"count":1}', t.end);
})

test.cb('should allow []', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp2')

  sletTest(app)
    .get('/?callback=callbacks[123]')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /callbacks\[123\]\(\{"count":1\}\);/, t.end);
})

test.cb('should ignore object callback parameter with jsonp', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp2')

  sletTest(app)
    .get('/2?clb=something')
    // .expect('Content-Type', 'application/json')
    .expect(200, '{"count":1}', t.end);
})

test.cb('should allow renaming callback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp3')

  sletTest(app)
    .get('/3?clb=something')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /something\(\{"count":1\}\);/, t.end);
})

test.cb('should disallow arbitrary js', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp4')

  sletTest(app)
    .get('/4?callback=foo;bar()')
    .expect('Content-Type', 'text/javascript; charset=utf-8')
    .expect(200, /foobar\(\{\}\);/, t.end);
})

test.cb('should escape utf whitespace', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp5')

  sletTest(app)
  .get('/5?callback=foo')
  .expect('Content-Type', 'text/javascript; charset=utf-8')
  .expect(200, /foo\(\{"str":"\\u2028 \\u2029 woot"\}\);/, t.end);
})

test.cb('should not escape utf whitespace for json fallback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp6')

  sletTest(app)
  .get('/6')
  .expect('Content-Type', 'application/json; charset=utf-8')
  .expect(200, '{"str":"\u2028 \u2029 woot"}', t.end);
})

test.cb('should include security header and prologue', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp')

  sletTest(app)
  .get('/?callback=something')
  .expect('Content-Type', 'text/javascript; charset=utf-8')
  .expect('X-Content-Type-Options', 'nosniff')
  .expect(200, /^\/\*\*\//, t.end);
})

test.cb('should not override previous Content-Types with no callback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp7')

  sletTest(app)
  .get('/7')
  .expect('Content-Type', 'application/vnd.example+json')
  // .expect(shouldNotHaveHeader('X-Content-Type-Options'))
  .expect(200, '{"hello":"world"}',  t.end);
})

test.cb('should override previous Content-Types with callback', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp7')

  sletTest(app)
  .get('/7?callback=cb')
  .expect('Content-Type', 'text/javascript; charset=utf-8')
  .expect('X-Content-Type-Options', 'nosniff')
  .expect(200, /cb\(\{"hello":"world"\}\);$/,  t.end);
})

test.cb('when given primitives', t => {
  const app = new Slet({
    root: __dirname,
    debug: false
  })

  app.router('fixtures/jsonp8')

  sletTest(app)
  .get('/8')
    .expect('Content-Type', 'application/json; charset=utf-8')
    .expect(200, 'null',  t.end);
})
<|endoftext|>


<commit_before>// PLUGIN: SPEAK
// SPEAK.JS by @kripken https://github.com/kripken/speak.js

(function ( Popcorn ) {

  /**
   * Popcorn speak: speaks text
   **/
  Popcorn.plugin( "speak", (function(){

    // Share a worker instance across instances to
    // relieve memory pressure.  When speakWorkerRefs
    // goes to 0, we can safely delete the instance.
    var speakWorker,
        speakWorkerRefs = 0;

    return {
      manifest: {
        about: {
          name: "Popcorn speak Plugin",
          version: "0.1",
          author: "Kate Hudson @k88hudson",
          website: "http://github.com/k88hudson"
        },
        options: {
          start: {
            elem: "input",
            type: "number",
            label: "In"
          },
          end: {
            elem: "input",
            type: "number",
            label: "Out"
          },
          text: {
            elem: "input",
            type: "text",
            label: "Text to speak:",
            "default": "Hello world"
          },
          showText: {
            elem: "input",
            type: "checkbox",
            label: "Show text?"
          },
          amplitude: {
            elem: "input",
            type: "number",
            label: "Amplitude:",
            default: 100
          },
          wordgap: {
            elem: "input",
            type: "number",
            label: "Wordgap:",
            default: 0
          },
          pitch: {
            elem: "input",
            type: "number",
            label: "Pitch:",
            default: 50
          },
          speed: {
            elem: "input",
            type: "number",
            label: "Speed:",
            default: 175
          },
          pluginPath: {
            elem: "input",
            type: "text",
            label: "Plugin path:",
            "default":"../shared/plugins/speak/",
            hidden: true
          }
        }
      },

      _setup: function( options ) {
        var target = options._target = document.getElementById( options.target ),
            speakOptions,
            context = this,
            manifestOptions = options._natives.manifest.options;

        if ( !target ) {
          target = document.createElement( "div" );
          target.id = options.target;
          context.media.parentNode.appendChild( target );
        }

        // SPEAK.JS by @kripken https://github.com/kripken/speak.js ---------------------------
        if( !speakWorker ){
          // Created a shared worker instance
          try {
            options.pluginPath = options.pluginPath || "js/plugins/speak/";
            speakWorker = new Worker( options.pluginPath + 'speakWorker.js' );
          } catch(e) {
            console.log( 'speak.js warning: no worker support' );
          }
        }

        // Bump instance ref count
        speakWorkerRefs++;

        function speak( text, args ) {
          // set to true if you want debug output on times.
          var PROFILE = false;

          function parseWav( wav ) {
            function readInt( i, bytes ) {
              var ret = 0,
                  shft = 0;

              while ( bytes ) {
                ret += wav[ i ] << shft;
                shft += 8;
                i++;
                bytes--;
              }
              return ret;
            }
            if ( readInt( 20, 2 ) !== 1 ) {
              throw 'Invalid compression code, not PCM';
            }
            if ( readInt( 22, 2 ) !== 1 ) {
              throw 'Invalid number of channels, not 1';
            }
            return {
              sampleRate: readInt( 24, 4 ),
              bitsPerSample: readInt( 34, 2 ),
              samples: wav.subarray( 44 )
            };
          }

          function generateAudio( wav ) {
            function encode64( data ) {
              var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
                  PAD = '=',
                  ret = '',
                  leftchar = 0,
                  leftbits = 0,
                  i,
                  curr;
              for( i = 0; i < data.length; i++ ) {
                leftchar = ( leftchar << 8 ) | data[ i ];
                leftbits += 8;
                while ( leftbits >= 6 ) {
                  curr = ( leftchar >> ( leftbits-6 ) ) & 0x3f;
                  leftbits -= 6;
                  ret += BASE[ curr ];
                }
              }
              if (leftbits === 2) {
                ret += BASE[ ( leftchar&3 ) << 4 ];
                ret += PAD + PAD;
              } else if ( leftbits === 4 ) {
                ret += BASE[ ( leftchar&0xf ) << 2 ];
                ret += PAD;
              }
              return ret;
            }

            var audio = new Audio();
            audio.src = "data:audio/x-wav;base64," + encode64( wav );
            return audio;
          }

          function handleWav( wav ) {
            var startTime = Date.now();
            var data = parseWav( wav ); // validate the data and parse it
            options._audio = generateAudio( wav );
            if ( PROFILE ) {
              console.log( 'speak.js: wav processing took ' + ( Date.now()-startTime ).toFixed( 2 ) + ' ms' );
            }
          }

          // Call the worker, which will return a wav that we then play
          var startTime = Date.now();
          speakWorker.onmessage = function( event ){
            if ( PROFILE ) {
              console.log( 'speak.js: worker processing took ' + ( Date.now()-startTime ).toFixed( 2 ) + ' ms' );
            }
            handleWav( event.data );
          };
          speakWorker.postMessage({ text: text, args: args });
        }

        // END SPEAK.JS---------------------------------------

        // Use manifest defaults if none provided
        speakOptions = {
          amplitude: options.amplitude || manifestOptions.amplitude.default,
          wordgap: options.wordgap || manifestOptions.wordgap.default,
          pitch: options.pitch || manifestOptions.pitch.default,
          speed: options.speed || manifestOptions.speed.default,
          target: options.target
        };

        // Use the manifest default in case none is given,
        // important in Butter for initial creation.
        options.text = options.text || manifestOptions.text.default;

        // Generate a default sound.
        speak( options.text, speakOptions );

        if( options.showText ) {
          options.showTextEl = document.createElement("span");
          options.showTextEl.innerHTML = options.text;
          options.showTextEl.style.display = "none";
          target.appendChild( options.showTextEl );
        }
      },

      start: function( event, options ) {
        if( options._audio ){
          options._audio.play();
        }
        if( options.showTextEl ){
          options.showTextEl.style.display = "block";
        }
      },

      end: function( event, options ) {
        if( options.showTextEl ){
          options.showTextEl.style.display = "none";
        }
      },

      _teardown: function( options ) {
        options._audio = null;

        if( options.showTextEl && options._target ) {
          options._target.removeChild( options.showTextEl );
        }

        // Decrease ref count on shared worker, delete if 0
        if( --speakWorkerRefs === 0 ){
          speakWorker = null;
        }
      }

    };
  }()));
}( Popcorn ));
<commit_msg>Debugging speak.js<commit_after>// PLUGIN: SPEAK
// SPEAK.JS by @kripken https://github.com/kripken/speak.js

(function ( Popcorn ) {

  /**
   * Popcorn speak: speaks text
   **/
  Popcorn.plugin( "speak", (function(){

    // Share a worker instance across instances to
    // relieve memory pressure.  When speakWorkerRefs
    // goes to 0, we can safely delete the instance.
    var speakWorker,
        speakWorkerRefs = 0;

    return {
      manifest: {
        about: {
          name: "Popcorn speak Plugin",
          version: "0.1",
          author: "Kate Hudson @k88hudson",
          website: "http://github.com/k88hudson"
        },
        options: {
          start: {
            elem: "input",
            type: "number",
            label: "In"
          },
          end: {
            elem: "input",
            type: "number",
            label: "Out"
          },
          text: {
            elem: "input",
            type: "text",
            label: "Text to speak:",
            "default": "Hello world"
          },
          showText: {
            elem: "input",
            type: "checkbox",
            label: "Show text?"
          },
          amplitude: {
            elem: "input",
            type: "number",
            label: "Amplitude:",
            default: 100
          },
          wordgap: {
            elem: "input",
            type: "number",
            label: "Wordgap:",
            default: 0
          },
          pitch: {
            elem: "input",
            type: "number",
            label: "Pitch:",
            default: 50
          },
          speed: {
            elem: "input",
            type: "number",
            label: "Speed:",
            default: 175
          },
          pluginPath: {
            elem: "input",
            type: "text",
            label: "Plugin path:",
            "default":"../shared/plugins/speak/",
            hidden: true
          }
        }
      },

      _setup: function( options ) {
        var target = options._target = document.getElementById( options.target ),
            speakOptions,
            context = this,
            manifestOptions = options._natives.manifest.options;

        if ( !target ) {
          target = document.createElement( "div" );
          target.id = options.target;
          context.media.parentNode.appendChild( target );
        }

        // SPEAK.JS by @kripken https://github.com/kripken/speak.js ---------------------------
        if( !speakWorker ){
          // Created a shared worker instance
          try {
            options.pluginPath = options.pluginPath || "js/plugins/speak/";
            speakWorker = new Worker( options.pluginPath + 'speakWorker.js' );
          } catch(e) {
            console.log( 'speak.js warning: no worker support' );
          }
        }

        // Bump instance ref count
        speakWorkerRefs++;

        function speak( text, args, options ) {
          // set to true if you want debug output on times.
          var PROFILE = false;

          function parseWav( wav ) {
            function readInt( i, bytes ) {
              var ret = 0,
                  shft = 0;

              while ( bytes ) {
                ret += wav[ i ] << shft;
                shft += 8;
                i++;
                bytes--;
              }
              return ret;
            }
            if ( readInt( 20, 2 ) !== 1 ) {
              throw 'Invalid compression code, not PCM';
            }
            if ( readInt( 22, 2 ) !== 1 ) {
              throw 'Invalid number of channels, not 1';
            }
            return {
              sampleRate: readInt( 24, 4 ),
              bitsPerSample: readInt( 34, 2 ),
              samples: wav.subarray( 44 )
            };
          }

          function generateAudio( wav ) {
            function encode64( data ) {
              var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
                  PAD = '=',
                  ret = '',
                  leftchar = 0,
                  leftbits = 0,
                  i,
                  curr;
              for( i = 0; i < data.length; i++ ) {
                leftchar = ( leftchar << 8 ) | data[ i ];
                leftbits += 8;
                while ( leftbits >= 6 ) {
                  curr = ( leftchar >> ( leftbits-6 ) ) & 0x3f;
                  leftbits -= 6;
                  ret += BASE[ curr ];
                }
              }
              if (leftbits === 2) {
                ret += BASE[ ( leftchar&3 ) << 4 ];
                ret += PAD + PAD;
              } else if ( leftbits === 4 ) {
                ret += BASE[ ( leftchar&0xf ) << 2 ];
                ret += PAD;
              }
              return ret;
            }

            var audio = new Audio();
            audio.src = "data:audio/x-wav;base64," + encode64( wav );
            return audio;
          }

          function handleWav( wav ) {
            var startTime = Date.now();
            var data = parseWav( wav ); // validate the data and parse it
            options._audio = generateAudio( wav );
            console.log('Audio created, setting on options._audio');
            if ( PROFILE ) {
              console.log( 'speak.js: wav processing took ' + ( Date.now()-startTime ).toFixed( 2 ) + ' ms' );
            }
          }

          // Call the worker, which will return a wav that we then play
          var startTime = Date.now();
          speakWorker.onmessage = function( event ){
            if ( PROFILE ) {
              console.log( 'speak.js: worker processing took ' + ( Date.now()-startTime ).toFixed( 2 ) + ' ms' );
            }
            handleWav( event.data );
          };
          speakWorker.postMessage({ text: text, args: args });
        }

        // END SPEAK.JS---------------------------------------

        // Use manifest defaults if none provided
        speakOptions = {
          amplitude: options.amplitude || manifestOptions.amplitude.default,
          wordgap: options.wordgap || manifestOptions.wordgap.default,
          pitch: options.pitch || manifestOptions.pitch.default,
          speed: options.speed || manifestOptions.speed.default,
          target: options.target
        };

        // We have to treat options.text differently, since it is used
        // to generate the wav and audio element.  When the user sets
        // new values on options.text, we need to trigger a rebuild
        // of the audio, so wrap in a getter/setter so we can catch
        // any changes to the options.
        var text = options.text || manifestOptions.text.default;
        delete options.text;
        Object.defineProperty( options, "text", {
          get: function(){
            return text;
          },
          set: function( aText ){
            if( !( aText && typeof aText === "string" ) ){
              return;
            }

            console.log('options.text setter, text=' + aText);

            text = aText;

            var speakOptions = {
              amplitude: options.amplitude || manifestOptions.amplitude.default,
              wordgap: options.wordgap || manifestOptions.wordgap.default,
              pitch: options.pitch || manifestOptions.pitch.default,
              speed: options.speed || manifestOptions.speed.default,
              target: options.target
            };

            speak( text, speakOptions, this );
          }
        });

        // Generate a default sound for butter.
        speak( options.text, speakOptions, options );

        if( options.showText ) {
          options.showTextEl = document.createElement("span");
          options.showTextEl.innerHTML = options.text;
          options.showTextEl.style.display = "none";
          target.appendChild( options.showTextEl );
        }
      },

      start: function( event, options ) {
        console.log('speak.start options._audio=' + options._audio);
        if( options._audio ){
          options._audio.play();
        }
        if( options.showTextEl ){
          options.showTextEl.style.display = "block";
        }
      },

      end: function( event, options ) {
        if( options.showTextEl ){
          options.showTextEl.style.display = "none";
        }
      },

      _teardown: function( options ) {
        options._audio = null;

        if( options.showTextEl && options._target ) {
          options._target.removeChild( options.showTextEl );
        }

        // Decrease ref count on shared worker, delete if 0
        if( --speakWorkerRefs === 0 ){
          speakWorker = null;
        }
      }

    };
  }()));
}( Popcorn ));
<|endoftext|>


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

/*
===========================================================================
  S E T T I N G    S T U F F    U P
===========================================================================
*/

var args = [];

process.argv.slice(2).forEach(function(arg) {
  var flag = arg.split('=')[0];

  switch (flag) {
    case 'debug':
      args.push('--nodeDebug');
      args.push('true');
      break;
    case '-d':
    case '--debug':
    case '--debug-brk':
      args.push('--v8Debug');
      args.push('true');
      break;
    default:
      args.push(arg);
      break;
  }
});

var fs = require('fs'),
    path = require('path'),
    os = require('os'),
    unzip = require('unzip'),
    Q = require('q'),
    request = require('request'),
    Download = require('download'),
    shelljs = require('shelljs/global'),
    _ = require('underscore');

/*
---------------------------------------------------------------------------
  S E T T I N G    S T U F F    U P
---------------------------------------------------------------------------
*/
var colors = require('colors');

colors.setTheme({
  silly: 'rainbow',
  input: 'grey',
  verbose: 'cyan',
  prompt: 'grey',
  info: 'green',
  data: 'grey',
  help: 'cyan',
  warn: 'yellow',
  debug: 'blue',
  error: 'red'
});

const knownTemplates2 = [
  {
    id   : 'ionic-shell',
    repo : 'http://github.com/MobileCaddy/shell-ionic/archive/master.zip',
    name : 'mobilecaddy-shell-ionic',
    desc : 'Empty Ioinc skeleton project'
  },
  {
    id   : 'ionic-seed-expenses',
    repo : 'https://github.com/MobileCaddy/seed-expenses-ionic/archive/master.zip',
    name : 'mobilecaddy-seed-expenses-ionic',
    desc : 'Ionic Time & Expenses App'
  }
];

/*
---------------------------------------------------------------------------
  H E L P
---------------------------------------------------------------------------
*/

// TODO use minimist instead
var optimist = require('optimist').
    usage('Usage: \n' +
      'mobilecaddy new <template|git-zip-url> <your-app-name>\n' +
      'mobilecaddy serve [options]\n' +
      'mobilecaddy templates').
    describe('help', 'Print MobileCaddy help menu').
    describe('version', 'Print MobileCaddy version').
    describe('local', 'Run \'serve\' against local mock data rather than SFDC').
    describe('rec', 'Record SFDC responses during a \'serve\' and populate \'mock\' files').
    describe('scrub=[true|false]', 'Clears local data (\'full\'=inc oauth) upon \'serve\'');

var argv = optimist.parse(args);

if (argv.help || argv.h) {
  optimist.showHelp();
  process.exit(0);
}

if (argv.version || argv.v) {
  console.log('mobilecaddy-cli : ' + require(path.join(__dirname, '../package.json')).version);
  try {
    json = JSON.parse(fs.readFileSync('package.json', 'utf8'));
    console.log('Current project ('+ json.name + ') : ' + json.version);
  } catch (err) {
    // do nothing
  }
  process.exit(0);
}

if (argv._.length === 0) {
  optimist.showHelp();
  process.exit(0);
}

/*
===========================================================================
  C H O O S I N G    T H E    C O M M A N D
===========================================================================
*/
switch (args[0]) {
  case 'new':
    runNew();
    break;
  case 'serve':
    runServe();
    break;
  case 'templates':
    runTemplates();
    break;
  default :
    runUnknownTask();
}

/*
===========================================================================
  N E W
===========================================================================
*/
function runNew(){
  if (args[1] && args[2]) {
    var targetPath = args[2];
    if(targetPath == '.') {
      console.error('Invalid target path, you may not specify \'.\' as an app name'.error);
      process.exit(0);
    }
    getTemplatesList().then(function(knownTemplates){
      var template = _.find(knownTemplates, function(el){return el.id == args[1];})
      if (!template) {
        if (validURL(args[1])) {
          template = {repo: args[1], name : "dummy"};
        }
      }
      if (template) {
        console.log('Cloning MobileCaddy template "' + args[1] + '" from ' + template.repo);
        fetchArchive(targetPath, template).then(function(){
          console.log('Clone complete'.info);
          return installNpmDeps(targetPath);
        }).then(function(){
          console.log('NPM dependancies installed'.info);
          cd(targetPath);
          return installBowerDeps(targetPath);
        }).then(function(){
          console.log('Bower dependancies installed'.info);
          return runInitGruntTask(targetPath)
        }).then(function(){
          console.log('Grunt tasks completed'.info);
          process.exit(0);
        }).catch(function(err){
          console.error(err.error);
          process.exit(0);
        });
      } else if (validURL(args[1])) {
        console.log("That looks like a URL, nice");
        process.exit(0)
      } else {
        console.error('Unknown template ' + args[1].error);
        process.exit(0);
      }
    });
  } else {
    console.error('You need more arguments, man!'.error);
    //optimist.showHelp();
    process.exit(0);
  }
}

function validURL(str) {
  var strRegex = "^((https|http|ftp)?://)"
        + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@
        + "(([0-9]{1,3}\.){3}[0-9]{1,3}" // IP形式的URL- 199.194.52.184
        + "|" // 允许IP和DOMAIN（域名）
        + "([0-9a-z_!~*'()-]+\.)*" // 域名- www.
        + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\." // 二级域名
        + "[a-z]{2,6})" // first level domain- .com or .museum
        + "(:[0-9]{1,4})?" // 端口- :80
        + "((/?)|" // a slash isn't required if there is no file name
        + "(/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+/?)$";
  var re=new RegExp(strRegex);
  if (!re.test(str)) return false; else return true;
}

/*
===========================================================================
  S E R V E
===========================================================================
*/
function runServe(){
  var scrub = "";
  if (argv.scrub) {
    scrub = "--scrub=" + argv.scrub;
  }
  var local = "";
  if (argv.local) {
    local = "--local=true";
  }
  var rec = "";
  if (argv.rec) {
    rec = "--rec";
  }
  var child = exec('grunt serve ' + scrub + ' ' + local + ' ' + rec, {async:true});
  child.stdout.on('data', function(data) {
  });
}

/*
===========================================================================
  T E M P L A T E S
===========================================================================
*/
function runTemplates(){
  getTemplatesList().then(function(knownTemplates){
    knownTemplates.forEach(function(el){
      console.log(el.id.info + ' : ' +el.desc);
    });
  });
}

/*
===========================================================================
  U N K N O W N    T A S K
===========================================================================
*/
function runUnknownTask(){
  console.error('Unknown task ' + args[0] + '!'.error);
  //optimist.showHelp();
  process.exit(0);
}

/*
 fetchArchive
 Desc: pulls git repo down and moves it into desired sub dir.
   Also does some small string replacement
 TODO : lots more can be done here based on CLI input
 */
function fetchArchive(targetPath, template) {
  var q = Q.defer();
  var replace = require("replace");

  var archiveUrl = template.repo
  // The folder name the project will be downloaded and extracted to
  var message = ['Downloading:'.bold, archiveUrl].join(' ');
  console.log(message);

  var tmpFolder = os.tmpdir();
  var tempZipFilePath = path.join(tmpFolder, 'mobilecaddy-' + new Date().getTime());

  new Download({mode: '755', extract: true})
    .get(archiveUrl)
    .dest(tempZipFilePath)
    .run(function (err, files) {
        if (!err) {
          fs.rename(files[0].path, targetPath,
            function(res){
              if (!res) {
                replace({
                    regex: template.name,
                    replacement: targetPath,
                    paths: [targetPath],
                    recursive: true,
                    silent: true,
                });
                q.resolve();
              } else {
                console.error('Renaming template repo failed '.error);
                q.reject(res);
              }
            })
        } else {
          console.error(err);
          q.reject(err);
        }
    });

  return q.promise;
}

function getTemplatesList(){
  var q = Q.defer();
  request({uri: 'http://developer.mobilecaddy.net/cli-templates.php', json : true}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      q.resolve(body);
    } else {
      console.error('Error retrieving known templates list\n'.error);
      console.error('These are the last known templates;');
      q.resolve(knownTemplates2);
    }
  });
  return q.promise;
}

function installNpmDeps(targetPath){
  var q = Q.defer();
  console.log('Installing project NPM dependancies (may take a few moments)...');
  cd(targetPath);
  var sudoStr = (os.platform() == "darwin" || argv.sudo) ? "sudo " : "";
  var child = exec(sudoStr + 'npm install', {async:true});
  child.stdout.on('data', function(data) {
    console.log('data: ' + data);
    q.resolve();
  });
  cd('..');
  return q.promise;
}

function installBowerDeps(targetPath){
  var q = Q.defer();
  console.log('Installing project bower dependancies (may take a few moments)...');
  var child = exec('bower install', function(code, output) {
    q.resolve();
  });
  return q.promise;
}

function runInitGruntTask(targetPath){
  var q = Q.defer();
  console.log('Running initial grunt tasks...');
  var child = exec('grunt devsetup', function(code, output) {
    q.resolve();
  });
  return q.promise;
}<commit_msg>Added help info about running sub-commands with sudo<commit_after>#!/usr/bin/env node

/*
===========================================================================
  S E T T I N G    S T U F F    U P
===========================================================================
*/

var args = [];

process.argv.slice(2).forEach(function(arg) {
  var flag = arg.split('=')[0];

  switch (flag) {
    case 'debug':
      args.push('--nodeDebug');
      args.push('true');
      break;
    case '-d':
    case '--debug':
    case '--debug-brk':
      args.push('--v8Debug');
      args.push('true');
      break;
    default:
      args.push(arg);
      break;
  }
});

var fs = require('fs'),
    path = require('path'),
    os = require('os'),
    unzip = require('unzip'),
    Q = require('q'),
    request = require('request'),
    Download = require('download'),
    shelljs = require('shelljs/global'),
    _ = require('underscore');

/*
---------------------------------------------------------------------------
  S E T T I N G    S T U F F    U P
---------------------------------------------------------------------------
*/
var colors = require('colors');

colors.setTheme({
  silly: 'rainbow',
  input: 'grey',
  verbose: 'cyan',
  prompt: 'grey',
  info: 'green',
  data: 'grey',
  help: 'cyan',
  warn: 'yellow',
  debug: 'blue',
  error: 'red'
});

const knownTemplates2 = [
  {
    id   : 'ionic-shell',
    repo : 'http://github.com/MobileCaddy/shell-ionic/archive/master.zip',
    name : 'mobilecaddy-shell-ionic',
    desc : 'Empty Ioinc skeleton project'
  },
  {
    id   : 'ionic-seed-expenses',
    repo : 'https://github.com/MobileCaddy/seed-expenses-ionic/archive/master.zip',
    name : 'mobilecaddy-seed-expenses-ionic',
    desc : 'Ionic Time & Expenses App'
  }
];

/*
---------------------------------------------------------------------------
  H E L P
---------------------------------------------------------------------------
*/

// TODO use minimist instead
var optimist = require('optimist').
    usage('Usage: \n' +
      'mobilecaddy new <template|git-zip-url> <your-app-name> [--sudo]\n' +
      'mobilecaddy serve [options]\n' +
      'mobilecaddy templates').
    describe('help', 'Print MobileCaddy help menu').
    describe('version', 'Print MobileCaddy version').
    describe('sudo', 'Run \'new\' command as root').
    describe('local', 'Run \'serve\' against local mock data rather than SFDC').
    describe('rec', 'Record SFDC responses during a \'serve\' and populate \'mock\' files').
    describe('scrub=[true|false]', 'Clears local data (\'full\'=inc oauth) upon \'serve\'');

var argv = optimist.parse(args);

if (argv.help || argv.h) {
  optimist.showHelp();
  process.exit(0);
}

if (argv.version || argv.v) {
  console.log('mobilecaddy-cli : ' + require(path.join(__dirname, '../package.json')).version);
  try {
    json = JSON.parse(fs.readFileSync('package.json', 'utf8'));
    console.log('Current project ('+ json.name + ') : ' + json.version);
  } catch (err) {
    // do nothing
  }
  process.exit(0);
}

if (argv._.length === 0) {
  optimist.showHelp();
  process.exit(0);
}

/*
===========================================================================
  C H O O S I N G    T H E    C O M M A N D
===========================================================================
*/
switch (args[0]) {
  case 'new':
    runNew();
    break;
  case 'serve':
    runServe();
    break;
  case 'templates':
    runTemplates();
    break;
  default :
    runUnknownTask();
}

/*
===========================================================================
  N E W
===========================================================================
*/
function runNew(){
  if (args[1] && args[2]) {
    var targetPath = args[2];
    if(targetPath == '.') {
      console.error('Invalid target path, you may not specify \'.\' as an app name'.error);
      process.exit(0);
    }
    getTemplatesList().then(function(knownTemplates){
      var template = _.find(knownTemplates, function(el){return el.id == args[1];})
      if (!template) {
        if (validURL(args[1])) {
          template = {repo: args[1], name : "dummy"};
        }
      }
      if (template) {
        console.log('Cloning MobileCaddy template "' + args[1] + '" from ' + template.repo);
        fetchArchive(targetPath, template).then(function(){
          console.log('Clone complete'.info);
          return installNpmDeps(targetPath);
        }).then(function(){
          console.log('NPM dependancies installed'.info);
          cd(targetPath);
          return installBowerDeps(targetPath);
        }).then(function(){
          console.log('Bower dependancies installed'.info);
          return runInitGruntTask(targetPath)
        }).then(function(){
          console.log('Grunt tasks completed'.info);
          process.exit(0);
        }).catch(function(err){
          console.error(err.error);
          process.exit(0);
        });
      } else if (validURL(args[1])) {
        console.log("That looks like a URL, nice");
        process.exit(0)
      } else {
        console.error('Unknown template ' + args[1].error);
        process.exit(0);
      }
    });
  } else {
    console.error('You need more arguments, man!'.error);
    //optimist.showHelp();
    process.exit(0);
  }
}

function validURL(str) {
  var strRegex = "^((https|http|ftp)?://)"
        + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@
        + "(([0-9]{1,3}\.){3}[0-9]{1,3}" // IP形式的URL- 199.194.52.184
        + "|" // 允许IP和DOMAIN（域名）
        + "([0-9a-z_!~*'()-]+\.)*" // 域名- www.
        + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\." // 二级域名
        + "[a-z]{2,6})" // first level domain- .com or .museum
        + "(:[0-9]{1,4})?" // 端口- :80
        + "((/?)|" // a slash isn't required if there is no file name
        + "(/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+/?)$";
  var re=new RegExp(strRegex);
  if (!re.test(str)) return false; else return true;
}

/*
===========================================================================
  S E R V E
===========================================================================
*/
function runServe(){
  var scrub = "";
  if (argv.scrub) {
    scrub = "--scrub=" + argv.scrub;
  }
  var local = "";
  if (argv.local) {
    local = "--local=true";
  }
  var rec = "";
  if (argv.rec) {
    rec = "--rec";
  }
  var child = exec('grunt serve ' + scrub + ' ' + local + ' ' + rec, {async:true});
  child.stdout.on('data', function(data) {
  });
}

/*
===========================================================================
  T E M P L A T E S
===========================================================================
*/
function runTemplates(){
  getTemplatesList().then(function(knownTemplates){
    knownTemplates.forEach(function(el){
      console.log(el.id.info + ' : ' +el.desc);
    });
  });
}

/*
===========================================================================
  U N K N O W N    T A S K
===========================================================================
*/
function runUnknownTask(){
  console.error('Unknown task ' + args[0] + '!'.error);
  //optimist.showHelp();
  process.exit(0);
}

/*
 fetchArchive
 Desc: pulls git repo down and moves it into desired sub dir.
   Also does some small string replacement
 TODO : lots more can be done here based on CLI input
 */
function fetchArchive(targetPath, template) {
  var q = Q.defer();
  var replace = require("replace");

  var archiveUrl = template.repo
  // The folder name the project will be downloaded and extracted to
  var message = ['Downloading:'.bold, archiveUrl].join(' ');
  console.log(message);

  var tmpFolder = os.tmpdir();
  var tempZipFilePath = path.join(tmpFolder, 'mobilecaddy-' + new Date().getTime());

  new Download({mode: '755', extract: true})
    .get(archiveUrl)
    .dest(tempZipFilePath)
    .run(function (err, files) {
        if (!err) {
          fs.rename(files[0].path, targetPath,
            function(res){
              if (!res) {
                replace({
                    regex: template.name,
                    replacement: targetPath,
                    paths: [targetPath],
                    recursive: true,
                    silent: true,
                });
                q.resolve();
              } else {
                console.error('Renaming template repo failed '.error);
                q.reject(res);
              }
            })
        } else {
          console.error(err);
          q.reject(err);
        }
    });

  return q.promise;
}

function getTemplatesList(){
  var q = Q.defer();
  request({uri: 'http://developer.mobilecaddy.net/cli-templates.php', json : true}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      q.resolve(body);
    } else {
      console.error('Error retrieving known templates list\n'.error);
      console.error('These are the last known templates;');
      q.resolve(knownTemplates2);
    }
  });
  return q.promise;
}

function installNpmDeps(targetPath){
  var q = Q.defer();
  console.log('Installing project NPM dependancies (may take a few moments)...');
  cd(targetPath);
  var sudoStr = (os.platform() == "darwin" || argv.sudo) ? "sudo " : "";
  var child = exec(sudoStr + 'npm install', {async:true});
  child.stdout.on('data', function(data) {
    console.log('data: ' + data);
    q.resolve();
  });
  cd('..');
  return q.promise;
}

function installBowerDeps(targetPath){
  var q = Q.defer();
  console.log('Installing project bower dependancies (may take a few moments)...');
  var child = exec('bower install', function(code, output) {
    q.resolve();
  });
  return q.promise;
}

function runInitGruntTask(targetPath){
  var q = Q.defer();
  console.log('Running initial grunt tasks...');
  var child = exec('grunt devsetup', function(code, output) {
    q.resolve();
  });
  return q.promise;
}<|endoftext|>


<commit_before>var fs = require('fs');
var _ = require('underscore');

if(_.isUndefined(global.buildNumber)){
	global.buildNumber = null;;
}
console.log("Build number ls " + global.buildNumber + " and build number: " + buildNumber);
// LOG convention:
// <cfg.log_file_root_path>/<cfg.log_file_name_prefix>_YYYYMMDD_HHNNSS_<build_number>

var zeroPad = function(num, numZeros) {
	var n = Math.abs(num);
	var zeros = Math.max(0, numZeros - Math.floor(n).toString().length );
	var zeroString = Math.pow(10,zeros).toString().substr(1);
	if( num < 0 ) {
		zeroString = '-' + zeroString;
	}

	return zeroString+n;
}

var getBuildNumber = function() {
	console.log("getBuildNumber " + buildNumber);
	if (buildNumber) {
		console.log("Return buildNumber" + buildNumber);
		return buildNumber;
	} else {
		var files = fs.readdirSync(cfg.log_file_root_path);
		var validLogFiles = _.filter(files, function(fileName){ return fileName.indexOf(cfg.log_file_name_prefix) >= 0; });
		if (validLogFiles.length == 0) {
			buildNumber = 0;
			console.log("Return 0");
			return buildNumber;
		}
		validLogFiles.sort();
		var latest = validLogFiles[validLogFiles.length-1];
		var latestArray = latest.split("_");
		buildNumber = (latestArray[latestArray.length-1]) * 1;
		console.log("Return F buildNumber" + buildNumber);
		return buildNumber;
	}
}

var getNextLogName = function() {
	console.log("getNextLogName");
	var d = new Date(); // local time. not utc
	var name =  cfg.log_file_name_prefix + "_" + d.getFullYear() + zeroPad(d.getMonth() +1, 2) + zeroPad(d.getDate(), 2) +
			"_" + zeroPad(d.getHours(), 2) + zeroPad(d.getMinutes(), 2) + zeroPad(d.getSeconds(), 2) + "_" + (getBuildNumber() + 1);
	console.log(" name: " + name);
	return name;
}

var advanceBuildNumber = function () {
	buildNumber = getBuildNumber();
	buildNumber++;
}

module.exports.getLatestLog = function(next) {
	fs.readdir(cfg.log_file_root_path, function(err, files) {
		if (err) {
			next(err);
		} else {
			var validLogFiles = _.filter(files, function(fileName){ return fileName.indexOf(cfg.log_file_name_prefix) >= 0; });
			if (validLogFiles.length == 0) {
				next({id:1, message:"No log file"});
			} else {
				validLogFiles.sort();
				var latest = validLogFiles[validLogFiles.length-1];
				next(null, {status:true, log:latest});
			}
		}
	});

}

module.exports.getAllLog = function(next) {
	fs.readdir(cfg.log_file_root_path, function(err, files) {
		if (err) {
			next(err);
		} else {
			var validLogFiles = _.filter(files, function(fileName){ return fileName.indexOf(cfg.log_file_name_prefix) >= 0; });
			if (validLogFiles.length == 0) {
				next({id:1, message:"No log file"});
			} else {
				validLogFiles.sort();
				next(null, {status:true, logs:validLogFiles});
			}
		}
	});
}

module.exports.getLogContent = function(param, next) {
	// data.name is the log name
	console.log("File Name: " + param.name);
	if (!param.name) {
		next({status:false, err: "File name is empty"});
	} else {
		var fileFullPath = cfg.log_file_root_path + param.name;
		fs.readFile(fileFullPath, function(err, data) {
			if (err) {
				next(err);
			} else {
				console.log("getLogContent " + JSON.stringify(data));
				next(null, {status:true, data:data});
			}
		});
	}
}

module.exports.saveLog = function(param, next) {
	// data.name is the log name
	// data.content is the log content
	if (!param.content) {
		next({status:false, err: "File content is empty"});
	} else {
		if (param.name) {
			var fileFullPath = cfg.log_file_root_path + param.name;
		} else {
			// if no name specify, then use the getNextLogName
			var fileFullPath = cfg.log_file_root_path + getNextLogName();
			advanceBuildNumber();
		}
		console.log("File full path: " + fileFullPath);
		fs.writeFile(fileFullPath, data.content, function(err) {
			if (err) {
				next(err);
			} else {
				next(null, {status:true});
			}
		});
	}
}
<commit_msg>fix typo<commit_after>var fs = require('fs');
var _ = require('underscore');

if(_.isUndefined(global.buildNumber)){
	global.buildNumber = null;;
}
console.log("Build number ls " + global.buildNumber + " and build number: " + buildNumber);
// LOG convention:
// <cfg.log_file_root_path>/<cfg.log_file_name_prefix>_YYYYMMDD_HHNNSS_<build_number>

var zeroPad = function(num, numZeros) {
	var n = Math.abs(num);
	var zeros = Math.max(0, numZeros - Math.floor(n).toString().length );
	var zeroString = Math.pow(10,zeros).toString().substr(1);
	if( num < 0 ) {
		zeroString = '-' + zeroString;
	}

	return zeroString+n;
}

var getBuildNumber = function() {
	console.log("getBuildNumber " + buildNumber);
	if (buildNumber) {
		console.log("Return buildNumber" + buildNumber);
		return buildNumber;
	} else {
		var files = fs.readdirSync(cfg.log_file_root_path);
		var validLogFiles = _.filter(files, function(fileName){ return fileName.indexOf(cfg.log_file_name_prefix) >= 0; });
		if (validLogFiles.length == 0) {
			buildNumber = 0;
			console.log("Return 0");
			return buildNumber;
		}
		validLogFiles.sort();
		var latest = validLogFiles[validLogFiles.length-1];
		var latestArray = latest.split("_");
		buildNumber = (latestArray[latestArray.length-1]) * 1;
		console.log("Return F buildNumber" + buildNumber);
		return buildNumber;
	}
}

var getNextLogName = function() {
	console.log("getNextLogName");
	var d = new Date(); // local time. not utc
	var name =  cfg.log_file_name_prefix + "_" + d.getFullYear() + zeroPad(d.getMonth() +1, 2) + zeroPad(d.getDate(), 2) +
			"_" + zeroPad(d.getHours(), 2) + zeroPad(d.getMinutes(), 2) + zeroPad(d.getSeconds(), 2) + "_" + (getBuildNumber() + 1);
	console.log(" name: " + name);
	return name;
}

var advanceBuildNumber = function () {
	buildNumber = getBuildNumber();
	buildNumber++;
}

module.exports.getLatestLog = function(next) {
	fs.readdir(cfg.log_file_root_path, function(err, files) {
		if (err) {
			next(err);
		} else {
			var validLogFiles = _.filter(files, function(fileName){ return fileName.indexOf(cfg.log_file_name_prefix) >= 0; });
			if (validLogFiles.length == 0) {
				next({id:1, message:"No log file"});
			} else {
				validLogFiles.sort();
				var latest = validLogFiles[validLogFiles.length-1];
				next(null, {status:true, log:latest});
			}
		}
	});

}

module.exports.getAllLog = function(next) {
	fs.readdir(cfg.log_file_root_path, function(err, files) {
		if (err) {
			next(err);
		} else {
			var validLogFiles = _.filter(files, function(fileName){ return fileName.indexOf(cfg.log_file_name_prefix) >= 0; });
			if (validLogFiles.length == 0) {
				next({id:1, message:"No log file"});
			} else {
				validLogFiles.sort();
				next(null, {status:true, logs:validLogFiles});
			}
		}
	});
}

module.exports.getLogContent = function(param, next) {
	// data.name is the log name
	console.log("File Name: " + param.name);
	if (!param.name) {
		next({status:false, err: "File name is empty"});
	} else {
		var fileFullPath = cfg.log_file_root_path + param.name;
		fs.readFile(fileFullPath, 'utf8', function(err, data) {
			if (err) {
				next(err);
			} else {
				console.log("getLogContent " + JSON.stringify(data));
				next(null, {status:true, data:data});
			}
		});
	}
}

module.exports.saveLog = function(param, next) {
	// data.name is the log name
	// data.content is the log content
	if (!param.content) {
		next({status:false, err: "File content is empty"});
	} else {
		if (param.name) {
			var fileFullPath = cfg.log_file_root_path + param.name;
		} else {
			// if no name specify, then use the getNextLogName
			var fileFullPath = cfg.log_file_root_path + getNextLogName();
			advanceBuildNumber();
		}
		console.log("File full path: " + fileFullPath);
		fs.writeFile(fileFullPath, data.content, 'utf8', function(err) {
			if (err) {
				next(err);
			} else {
				next(null, {status:true});
			}
		});
	}
}
<|endoftext|>


<commit_before>'use strict';

var commander = require('commander');
var constants = require('./constants');
var env = require('./env');

/**
 * Assert that the program contains a valid number of arguments.
 * @private
 * @param {object} program
 * @returns {undefined}
 * @throws {Error}
 */
function assertValidArgumentCount(program) {
  var max = program.bump ? 2 : 3;
  if (!program.args.length > max) {
    throw new Error('Too many arguments');
  }
}

/**
 * Start the program.
 * @param {object} process
 * @param {object} pkg
 * @param {object} config
 * @returns {object}
 * @throws {Error}
 */
function cli(process, pkg, config) {
  var program = commander
    .version(pkg.version)
    .description(
  'release is a tool for releasing software. It supports bumping version numbers\n' +
'  in JavaScript projects out-of-the-box, but is otherwise generic enough to\n' +
'  release any kind of software. Run release with no arguments for interactive\n' +
'  mode.\n\n' +
'  For more information, refer to the README.')
    .usage('[Options...] [CURRENT_VERSION] [RELEASE_VERSION] [DEVELOPMENT_VERSION]')
    .option('-b, --branch', 'the branch to release from (defaults to the current branch)')
    .option('--bump', 'bump the version number from version FROM ' +
                      'to\n                       version TO ')
    .option('-n, --non-interactive', 'run in non-interactive mode ' +
                                     '(e.g., in a script)')
    .option('-p, --publish', 'execute the publish plan')
    .option('-s, --slug', 'specify the repository slug ' +
                          '(owner_name/repo_name)')
    .option('-t, --token', 'assign the Travis CI token to use')
    .option('-x, --execute', 'execute the plans (defaults to true unless using Travis CI)')

  // env.addCommandLineOptionsForVariablesUnassignedInConfigPlans(program, config,
  //   constants.excluding);

  program = program.parse(process.argv);
  assertValidArgumentCount(program);
  return program;
}

module.exports = cli;
<commit_msg>Add missing parameter to branch<commit_after>'use strict';

var commander = require('commander');
var constants = require('./constants');
var env = require('./env');

/**
 * Assert that the program contains a valid number of arguments.
 * @private
 * @param {object} program
 * @returns {undefined}
 * @throws {Error}
 */
function assertValidArgumentCount(program) {
  var max = program.bump ? 2 : 3;
  if (!program.args.length > max) {
    throw new Error('Too many arguments');
  }
}

/**
 * Start the program.
 * @param {object} process
 * @param {object} pkg
 * @param {object} config
 * @returns {object}
 * @throws {Error}
 */
function cli(process, pkg, config) {
  var program = commander
    .version(pkg.version)
    .description(
  'release is a tool for releasing software. It supports bumping version numbers\n' +
'  in JavaScript projects out-of-the-box, but is otherwise generic enough to\n' +
'  release any kind of software. Run release with no arguments for interactive\n' +
'  mode.\n\n' +
'  For more information, refer to the README.')
    .usage('[Options...] [CURRENT_VERSION] [RELEASE_VERSION] [DEVELOPMENT_VERSION]')
    .option('-b, --branch [branch]', 'the branch to release from (defaults to the current branch)')
    .option('--bump', 'bump the version number from version FROM ' +
                      'to\n                       version TO ')
    .option('-n, --non-interactive', 'run in non-interactive mode ' +
                                     '(e.g., in a script)')
    .option('-p, --publish', 'execute the publish plan')
    .option('-s, --slug', 'specify the repository slug ' +
                          '(owner_name/repo_name)')
    .option('-t, --token', 'assign the Travis CI token to use')
    .option('-x, --execute', 'execute the plans (defaults to true unless using Travis CI)')

  // env.addCommandLineOptionsForVariablesUnassignedInConfigPlans(program, config,
  //   constants.excluding);

  program = program.parse(process.argv);
  assertValidArgumentCount(program);
  return program;
}

module.exports = cli;
<|endoftext|>


<commit_before>var errors = require('./errors'),
        fs = require('fs'),
         _ = require('lodash')

var usage = "Usage: pig COMMAND NAME [& args]\n\n" +
            "Commands:\n" +
            "  start   Start container\n" +
            "  stop    Stop container\n" +
            "  logs    Show docker logs output for container (use -f for follow, -t for tail)\n" +
            "  inspect Show docker inspect output for container\n" +
            "  up      Start all daemons from pig.json\n" +
            "  down    Stop all daemons in pig.json\n" +
            "  bash    Attach /bin/bash to container (for debug)\n"

function parseArgs(args) {
    var command     = args[0]
    var recreate    = _.contains(['--no-recreate', '-R'], args[1]) == false
    var name        = recreate ? args[1] : args[2]
    var remainder   = args.slice(recreate ? 2 : 3)
    var interactive = !(process.env["NONINTERACTIVE"] === 'true')
    var verbose     = process.env["VERBOSE"] === 'true'

    return {
        command: command,
        name: name,
        remainder: remainder,
        options: { interactive: interactive, verbose: verbose, recreate: recreate },
    }
}

function getContainer(config, args) {
    if (!args.name) {
        throw new errors.PigError('No container name given\n' + usage)
    }

    var container = config[args.name]
    if (container) {
        return container
    } else {
        throw new errors.PigError("Container '" + args.name + "' not found")
    }
}

function handleError(err) {
    if (err instanceof errors.ConfigError) {
        process.stderr.write('error in configuration: ' + err.message + '\n')
        process.exit(1)
    } else if (err instanceof errors.PigError) {
        process.stderr.write('error: ' + err.message + '\n')
        process.exit(1)
    } else if (err) {
        throw err
    }
}

function loadConfiguration(){
  if (fs.existsSync('pig.json') && fs.existsSync('pig.yml')) {
    throw new errors.ConfigError("Can't decide configuration file to load. Both pig.json and pig.yml present. Please remove either one.");
  } else if (fs.existsSync('pig.json')) {
    return require('./config').fromFile('pig.json')
  } else if (fs.existsSync('pig.yml')) {
    return require('./config').fromFile('pig.yml')
  } else {
    throw new errors.ConfigError('Could not find pig.json or pig.yml from current working directory.')
  }
}

function main(args, callback) {
    var config = loadConfiguration()
    var commands = require('./commands')(config, args.options)

    switch (args.command) {
        case "start":
            commands.start(getContainer(config, args), args.remainder, { recreate: args.options.recreate }, callback)
            break

        case "stop":
            commands.stop(getContainer(config, null, args), callback)
            break

        case "bash":
            commands.bash(getContainer(config, args), callback)
            break

        case "up":
            commands.startDaemons({ recreate: args.options.recreate }, callback)
            break

        case "down":
            commands.stopDaemons(callback)
            break

        case "logs":
        case "inspect":
            commands.docker(args.command, args.remainder.concat([getContainer(config, args).name]), callback)
            break

        case undefined:
            callback(new errors.PigError('No command specified\n' + usage))
            break

        default:
            callback(new errors.PigError('Unknown command \'' + args.command + '\'\n' + usage))
            break
    }
}

module.exports.main = function() {
    try {
        var cmdArgs = parseArgs(process.argv.slice(2))
        main(cmdArgs, handleError)
    } catch (err) {
        handleError(err)
    }
}
<commit_msg>Fix incorrect argument placement from stop command<commit_after>var errors = require('./errors'),
        fs = require('fs'),
         _ = require('lodash')

var usage = "Usage: pig COMMAND NAME [& args]\n\n" +
            "Commands:\n" +
            "  start   Start container\n" +
            "  stop    Stop container\n" +
            "  logs    Show docker logs output for container (use -f for follow, -t for tail)\n" +
            "  inspect Show docker inspect output for container\n" +
            "  up      Start all daemons from pig.json\n" +
            "  down    Stop all daemons in pig.json\n" +
            "  bash    Attach /bin/bash to container (for debug)\n"

function parseArgs(args) {
    var command     = args[0]
    var recreate    = _.contains(['--no-recreate', '-R'], args[1]) == false
    var name        = recreate ? args[1] : args[2]
    var remainder   = args.slice(recreate ? 2 : 3)
    var interactive = !(process.env["NONINTERACTIVE"] === 'true')
    var verbose     = process.env["VERBOSE"] === 'true'

    return {
        command: command,
        name: name,
        remainder: remainder,
        options: { interactive: interactive, verbose: verbose, recreate: recreate },
    }
}

function getContainer(config, args) {
    if (!args.name) {
        throw new errors.PigError('No container name given\n' + usage)
    }

    var container = config[args.name]
    if (container) {
        return container
    } else {
        throw new errors.PigError("Container '" + args.name + "' not found")
    }
}

function handleError(err) {
    if (err instanceof errors.ConfigError) {
        process.stderr.write('error in configuration: ' + err.message + '\n')
        process.exit(1)
    } else if (err instanceof errors.PigError) {
        process.stderr.write('error: ' + err.message + '\n')
        process.exit(1)
    } else if (err) {
        throw err
    }
}

function loadConfiguration(){
  if (fs.existsSync('pig.json') && fs.existsSync('pig.yml')) {
    throw new errors.ConfigError("Can't decide configuration file to load. Both pig.json and pig.yml present. Please remove either one.");
  } else if (fs.existsSync('pig.json')) {
    return require('./config').fromFile('pig.json')
  } else if (fs.existsSync('pig.yml')) {
    return require('./config').fromFile('pig.yml')
  } else {
    throw new errors.ConfigError('Could not find pig.json or pig.yml from current working directory.')
  }
}

function main(args, callback) {
    var config = loadConfiguration()
    var commands = require('./commands')(config, args.options)

    switch (args.command) {
        case "start":
            commands.start(getContainer(config, args), args.remainder, { recreate: args.options.recreate }, callback)
            break

        case "stop":
            commands.stop(getContainer(config, args), null, callback)
            break

        case "bash":
            commands.bash(getContainer(config, args), callback)
            break

        case "up":
            commands.startDaemons({ recreate: args.options.recreate }, callback)
            break

        case "down":
            commands.stopDaemons(callback)
            break

        case "logs":
        case "inspect":
            commands.docker(args.command, args.remainder.concat([getContainer(config, args).name]), callback)
            break

        case undefined:
            callback(new errors.PigError('No command specified\n' + usage))
            break

        default:
            callback(new errors.PigError('Unknown command \'' + args.command + '\'\n' + usage))
            break
    }
}

module.exports.main = function() {
    try {
        var cmdArgs = parseArgs(process.argv.slice(2))
        main(cmdArgs, handleError)
    } catch (err) {
        handleError(err)
    }
}
<|endoftext|>