<commit_before>var Gdax = require('gdax');
var util = require('../core/util.js');
var _ = require('lodash');
var moment = require('moment');
var log = require('../core/log');

var batchSize = 100;

var Trader = function(config) {
    _.bindAll(this);

    this.post_only = false;
    this.use_sandbox = false;
    this.name = 'GDAX';
    this.import = false;
    this.scanback = false;
    this.scanbackTid = 0;
    this.scanbackResults = [];

    if(_.isObject(config)) {
        this.key = config.key;
        this.secret = config.secret;
        this.passphrase = config.passphrase;

        this.pair = [config.asset, config.currency].join('-').toUpperCase();
        this.post_only = config.post_only ? config.post_only : false;
    }

    this.gdax_public = new Gdax.PublicClient(this.pair, this.use_sandbox ? 'https://api-public.sandbox.gdax.com' : undefined);
    this.gdax = new Gdax.AuthenticatedClient(this.key, this.secret, this.passphrase, this.use_sandbox ? 'https://api-public.sandbox.gdax.com' : undefined);
}

// if the exchange errors we try the same call again after
// waiting 10 seconds
Trader.prototype.retry = function(method, args) {
    var wait = +moment.duration(10, 'seconds');
    log.debug(this.name, 'returned an error, retrying..');

    var self = this;

    // make sure the callback (and any other fn)
    // is bound to Trader
    _.each(args, function(arg, i) {
        if(_.isFunction(arg))
            args[i] = _.bind(arg, self);
    });

    // run the failed method again with the same
    // arguments after wait
    setTimeout(
        function() { method.apply(self, args) },
        wait
    );
}

Trader.prototype.getPortfolio = function(callback) {
    var result = function(err, response, data) {

        if(_.has(data, 'message')) {
            if(data.message === 'Invalid API Key' || data.message === 'Invalid Passphrase')
                util.die('GDAX said: ' + data.message);

            return callback(data.message, []);
        }

        var portfolio = data.map(function (account) {
                return {
                    name: account.currency.toUpperCase(),
                    amount: parseFloat(account.available)
                }
            }
        );
        callback(err, portfolio);
    };

    this.gdax.getAccounts(result);
}

Trader.prototype.getTicker = function(callback) {
    var result = function(err, response, data) {
        callback(err, {bid: +data.bid, ask: +data.ask})
    };

    this.gdax_public.getProductTicker(result);
}

Trader.prototype.getFee = function(callback) {
    //https://www.gdax.com/fees
    //There is no maker fee, not sure if we need taker fee here
    //If post only is enabled, gdax only does maker trades which are free
    callback(false, this.post_only ? 0 : 0.0025);
}

Trader.prototype.buy = function(amount, price, callback) {
    var args = _.toArray(arguments);
    var buyParams = {
        'price': price,
        'size': amount,
        'product_id': this.pair,
        'post_only': this.post_only
    };
    var result = function(err, response, data) {
        if (err || data.hasOwnProperty('message')) {
            log.error('Error buying at GDAX:', data.message, error);
            return this.retry(this.buy, args);
        }

        callback(err, data.id);
    };

    this.gdax.buy(buyParams, result);
}

Trader.prototype.sell = function(amount, price, callback) {
    var sellParams = {
        'price': price,
        'size': amount,
        'product_id': this.pair,
        'post_only': this.post_only
    };
    var result = function(err, response, data) {
        if (err || data.hasOwnProperty('message')) {
            log.error('Error selling at GDAX:', data.message, error);
            return this.retry(this.sell, args);
        }
        callback(err, data.id);
    };

    this.gdax.sell(sellParams, result);
}

Trader.prototype.checkOrder = function(order, callback) {

    var args = _.toArray(arguments);
    if (order == null) {
        return callback('EMPTY ORDER_ID', false);
    }

    var result = function(err, response, data) {
        if(err || (data && data.message)) {
            log.error('GDAX ERROR:', err, data.message);
            return this.retry(this.checkOrder, args);
        }

        var status = data.status;
        if (status == 'done') {
            return callback(err, true);
        } else if (status == 'rejected') {
            return callback(err, false);
        } else if (status == 'pending') {
            return callback(err, false);
        }
        callback(err, false);
    }.bind(this);

    this.gdax.getOrder(order, result);
}

Trader.prototype.getOrder = function(order, callback) {

    var args = _.toArray(arguments);
    if (order == null) {
        return callback('EMPTY ORDER_ID', false);
    }

    var result = function(err, response, data) {
        if(err || (data && data.message)) {
            log.error('GDAX ERROR:', err, data.message);
            return this.retry(this.checkOrder, args);
        }

        var price = parseFloat( data.price );
        var amount = parseFloat( data.filled_size );
        var date = moment( data.done_at );

        callback(undefined, {price, amount, date});
    }.bind(this);

    this.gdax.getOrder(order, result);
}

Trader.prototype.cancelOrder = function(order, callback) {
    if (order == null) {
        return;
    }

    var result = function(err, response, data) {
        // todo..
        callback();
    };

    this.gdax.cancelOrder(order, result);
}

Trader.prototype.getTrades = function(since, callback, descending) {
    var args = _.toArray(arguments);
    var lastScan = 0;

    var process = function(err, response, data) {
        if(err)
            return this.retry(this.getTrades, args);

        var result = _.map(data, function(trade) {
            return {
                tid: trade.trade_id,
                amount: parseFloat(trade.size),
                date: moment.utc(trade.time).format('X'),
                price: parseFloat(trade.price)
            };
        });

        if (this.scanback) {
            var last = _.last(data);
            var first = _.first(data);

            // Try to find trade id matching the since date
            if (!this.scanbackTid) {
                // either scan for new ones or we found it.
                if (moment.utc(last.time) < moment.utc(since)) {
                    this.scanbackTid = last.trade_id;
                } else {
                    log.debug('Scanning backwards...' + last.time);
                    this.gdax_public.getProductTrades({after: last.trade_id - (batchSize * lastScan) , limit: batchSize}, process);
                    lastScan++;
                    if (lastScan > 100) {
                        lastScan = 10;
                    }
                }
            }

            if (this.scanbackTid) {
            // if scanbackTid is set we need to move forward again
                log.debug('Backwards: ' + last.time + ' (' + last.trade_id + ') to ' + first.time + ' (' + first.trade_id + ')');

                if (this.import) {
                    this.scanbackTid = first.trade_id;
                    callback(null, result.reverse());
                } else {
                    this.scanbackResults = this.scanbackResults.concat(result.reverse());

                    if (this.scanbackTid != first.trade_id) {
                        this.scanbackTid = first.trade_id;
                        this.gdax_public.getProductTrades({after: this.scanbackTid + batchSize + 1, limit: batchSize}, process);
                    } else {
                        this.scanback = false;
                        this.scanbackTid = 0;
                        if (!this.import) {
                            log.debug('Scan finished: data found:' + this.scanbackResults.length);
                            callback(null, this.scanbackResults);
                        }
                        this.scanbackResults = [];
                    }
                }
            }
        } else {
            callback(null, result.reverse());
        }
    }.bind(this);

    if (since || this.scanback) {
        this.scanback = true;
        if (this.scanbackTid) {
            this.gdax_public.getProductTrades({after: this.scanbackTid + batchSize + 1, limit: batchSize}, process);
        } else {
            log.debug('Scanning back in the history needed...');
            log.debug(moment.utc(since).format());
            this.gdax_public.getProductTrades({limit: batchSize}, process);
        }
    } else {
        this.gdax_public.getProductTrades({limit: batchSize}, process);
    }

}

Trader.getCapabilities = function () {
  return {
    name: 'GDAX',
    slug: 'gdax',
    currencies: ['USD', 'EUR', 'GBP', 'BTC'],
    assets: ['BTC', 'LTC', 'ETH'],
    markets: [
      { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['USD', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['USD', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['EUR', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['GBP', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } }
    ],
    requires: ['key', 'secret', 'passphrase'],
    providesHistory: 'date',
    providesFullHistory: true,
    tid: 'tid',
    tradable: true
  };
}

module.exports = Trader;<commit_msg>Fix undefined errors in gdax.js<commit_after>var Gdax = require('gdax');
var util = require('../core/util.js');
var _ = require('lodash');
var moment = require('moment');
var log = require('../core/log');

var batchSize = 100;

var Trader = function(config) {
    _.bindAll(this);

    this.post_only = false;
    this.use_sandbox = false;
    this.name = 'GDAX';
    this.import = false;
    this.scanback = false;
    this.scanbackTid = 0;
    this.scanbackResults = [];

    if(_.isObject(config)) {
        this.key = config.key;
        this.secret = config.secret;
        this.passphrase = config.passphrase;

        this.pair = [config.asset, config.currency].join('-').toUpperCase();
        this.post_only = config.post_only ? config.post_only : false;
    }

    this.gdax_public = new Gdax.PublicClient(this.pair, this.use_sandbox ? 'https://api-public.sandbox.gdax.com' : undefined);
    this.gdax = new Gdax.AuthenticatedClient(this.key, this.secret, this.passphrase, this.use_sandbox ? 'https://api-public.sandbox.gdax.com' : undefined);
}

// if the exchange errors we try the same call again after
// waiting 10 seconds
Trader.prototype.retry = function(method, args) {
    var wait = +moment.duration(10, 'seconds');
    log.debug(this.name, 'returned an error, retrying..');

    var self = this;

    // make sure the callback (and any other fn)
    // is bound to Trader
    _.each(args, function(arg, i) {
        if(_.isFunction(arg))
            args[i] = _.bind(arg, self);
    });

    // run the failed method again with the same
    // arguments after wait
    setTimeout(
        function() { method.apply(self, args) },
        wait
    );
}

Trader.prototype.getPortfolio = function(callback) {
    var result = function(err, response, data) {

        if(_.has(data, 'message')) {
            if(data.message === 'Invalid API Key' || data.message === 'Invalid Passphrase')
                util.die('GDAX said: ' + data.message);

            return callback(data.message, []);
        }

        var portfolio = data.map(function (account) {
                return {
                    name: account.currency.toUpperCase(),
                    amount: parseFloat(account.available)
                }
            }
        );
        callback(err, portfolio);
    };

    this.gdax.getAccounts(result);
}

Trader.prototype.getTicker = function(callback) {
    var result = function(err, response, data) {
        callback(err, {bid: +data.bid, ask: +data.ask})
    };

    this.gdax_public.getProductTicker(result);
}

Trader.prototype.getFee = function(callback) {
    //https://www.gdax.com/fees
    //There is no maker fee, not sure if we need taker fee here
    //If post only is enabled, gdax only does maker trades which are free
    callback(false, this.post_only ? 0 : 0.0025);
}

Trader.prototype.buy = function(amount, price, callback) {
    var args = _.toArray(arguments);
    var buyParams = {
        'price': price,
        'size': amount,
        'product_id': this.pair,
        'post_only': this.post_only
    };
    var result = function(err, response, data) {
        if (err || data.hasOwnProperty('message')) {
            log.error('Error buying at GDAX:', err || data.message);
            return this.retry(this.buy, args);
        }
        callback(err, data.id);
    }.bind(this);

    this.gdax.buy(buyParams, result);
}

Trader.prototype.sell = function(amount, price, callback) {
    var args = _.toArray(arguments);
    var sellParams = {
        'price': price,
        'size': amount,
        'product_id': this.pair,
        'post_only': this.post_only
    };
    var result = function(err, response, data) {
        if (err || data.hasOwnProperty('message')) {
            log.error('Error selling at GDAX:', err || data.message);
            return this.retry(this.sell, args);
        }
        callback(err, data.id);
    }.bind(this);

    this.gdax.sell(sellParams, result);
}

Trader.prototype.checkOrder = function(order, callback) {
    var args = _.toArray(arguments);

    if (order == null) {
        return callback('EMPTY ORDER_ID', false);
    }

    var result = function(err, response, data) {
        if (err || data.hasOwnProperty('message')) {
            log.error('GDAX ERROR:', err || data.message);
            return this.retry(this.checkOrder, args);
        }

        var status = data.status;
        if (status == 'done') {
            return callback(err, true);
        } else if (status == 'rejected') {
            return callback(err, false);
        } else if (status == 'pending') {
            return callback(err, false);
        }
        callback(err, false);
    }.bind(this);

    this.gdax.getOrder(order, result);
}

Trader.prototype.getOrder = function(order, callback) {
    var args = _.toArray(arguments);
    if (order == null) {
        return callback('EMPTY ORDER_ID', false);
    }

    var result = function(err, response, data) {
        if (err || data.hasOwnProperty('message')) {
            log.error('GDAX ERROR:', err || data.message);
            return this.retry(this.checkOrder, args);
        }

        var price = parseFloat( data.price );
        var amount = parseFloat( data.filled_size );
        var date = moment( data.done_at );

        callback(undefined, {price, amount, date});
    }.bind(this);

    this.gdax.getOrder(order, result);
}

Trader.prototype.cancelOrder = function(order, callback) {
    if (order == null) {
        return;
    }

    var result = function(err, response, data) {
        // todo..
        callback();
    };

    this.gdax.cancelOrder(order, result);
}

Trader.prototype.getTrades = function(since, callback, descending) {
    var args = _.toArray(arguments);
    var lastScan = 0;

    var process = function(err, response, data) {
        if(err)
            return this.retry(this.getTrades, args);

        var result = _.map(data, function(trade) {
            return {
                tid: trade.trade_id,
                amount: parseFloat(trade.size),
                date: moment.utc(trade.time).format('X'),
                price: parseFloat(trade.price)
            };
        });

        if (this.scanback) {
            var last = _.last(data);
            var first = _.first(data);

            // Try to find trade id matching the since date
            if (!this.scanbackTid) {
                // either scan for new ones or we found it.
                if (moment.utc(last.time) < moment.utc(since)) {
                    this.scanbackTid = last.trade_id;
                } else {
                    log.debug('Scanning backwards...' + last.time);
                    this.gdax_public.getProductTrades({after: last.trade_id - (batchSize * lastScan) , limit: batchSize}, process);
                    lastScan++;
                    if (lastScan > 100) {
                        lastScan = 10;
                    }
                }
            }

            if (this.scanbackTid) {
            // if scanbackTid is set we need to move forward again
                log.debug('Backwards: ' + last.time + ' (' + last.trade_id + ') to ' + first.time + ' (' + first.trade_id + ')');

                if (this.import) {
                    this.scanbackTid = first.trade_id;
                    callback(null, result.reverse());
                } else {
                    this.scanbackResults = this.scanbackResults.concat(result.reverse());

                    if (this.scanbackTid != first.trade_id) {
                        this.scanbackTid = first.trade_id;
                        this.gdax_public.getProductTrades({after: this.scanbackTid + batchSize + 1, limit: batchSize}, process);
                    } else {
                        this.scanback = false;
                        this.scanbackTid = 0;
                        if (!this.import) {
                            log.debug('Scan finished: data found:' + this.scanbackResults.length);
                            callback(null, this.scanbackResults);
                        }
                        this.scanbackResults = [];
                    }
                }
            }
        } else {
            callback(null, result.reverse());
        }
    }.bind(this);

    if (since || this.scanback) {
        this.scanback = true;
        if (this.scanbackTid) {
            this.gdax_public.getProductTrades({after: this.scanbackTid + batchSize + 1, limit: batchSize}, process);
        } else {
            log.debug('Scanning back in the history needed...');
            log.debug(moment.utc(since).format());
            this.gdax_public.getProductTrades({limit: batchSize}, process);
        }
    } else {
        this.gdax_public.getProductTrades({limit: batchSize}, process);
    }

}

Trader.getCapabilities = function () {
  return {
    name: 'GDAX',
    slug: 'gdax',
    currencies: ['USD', 'EUR', 'GBP', 'BTC'],
    assets: ['BTC', 'LTC', 'ETH'],
    markets: [
      { pair: ['USD', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['USD', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['USD', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['EUR', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['GBP', 'BTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
      { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } }
    ],
    requires: ['key', 'secret', 'passphrase'],
    providesHistory: 'date',
    providesFullHistory: true,
    tid: 'tid',
    tradable: true
  };
}

module.exports = Trader;<|endoftext|>


<commit_before>/*
 | Mix Asset Management
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your application.
 |
 | Docs: https://github.com/JeffreyWay/laravel-mix/tree/master/docs#readme
 */

let mix = require("laravel-mix");

mix
  // JS entry file. Supports Vue, and uses Babel
  //
  // More info and options (like React support) here:
  // https://github.com/JeffreyWay/laravel-mix/blob/master/docs/mixjs.md
  .js("static/js/app.js", "public/js")
  // SASS entry file. Uses autoprefixer automatically.
  .sass("static/css/app.scss", "public/css")
  // Customize postCSS:
  // https://github.com/JeffreyWay/laravel-mix/blob/master/docs/css-preprocessors.md#postcss-plugins
  .options({
    // http://lostgrid.org Simple and powerful CSS grids
    postCss: [require("lost")],
    // If you want to process images, change this to true and add options from
    // https://github.com/tcoopman/image-webpack-loader
    imgLoaderOptions: { enabled: false }
  })
  // Set public path so manifest gets output here
  .setPublicPath("public")
  // Copies static assets like images and fonts
  .copyDirectory("static/assets", "public/assets")
  // Add assets to the manifest
  .version(["public/assets"])
  // Silence most output from webpack
  .webpackConfig({ stats: "errors-only" })
  .disableSuccessNotifications();

// Full API
// Docs: https://github.com/JeffreyWay/laravel-mix/tree/master/docs#readme
//
// mix.js(src, output);
// mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
// mix.preact(src, output); <-- Identical to mix.js(), but registers Preact compilation.
// mix.coffee(src, output); <-- Identical to mix.js(), but registers CoffeeScript compilation.
// mix.ts(src, output); <-- TypeScript support. Requires tsconfig.json to exist in the same folder as webpack.mix.js
// mix.extract(vendorLibs);
// mix.sass(src, output);
// mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack.
// mix.fastSass('src', output); <-- Alias for mix.standaloneSass().
// mix.less(src, output);
// mix.stylus(src, output);
// mix.postCss(src, output, [require('postcss-some-plugin')()]);
// mix.browserSync('my-site.test');
// mix.combine(files, destination);
// mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
// mix.copy(from, to);
// mix.copyDirectory(fromDir, toDir);
// mix.minify(file);
// mix.sourceMaps(); // Enable sourcemaps
// mix.version(); // Enable versioning.
// mix.disableNotifications();
// mix.disableSuccessNotifications();
// mix.setPublicPath('path/to/public');
// mix.setResourceRoot('prefix/for/resource/locators');
// mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
// mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
// mix.babelConfig({}); <-- Merge extra Babel configuration (plugins, etc.) with Mix's default.
// mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
// mix.extend(name, handler) <-- Extend Mix's API with your own components.
// mix.options({
//   extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
//   globalVueStyles: file, // Variables file to be imported in every component.
//   processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
//   purifyCss: false, // Remove unused CSS selectors.
//   uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
//   postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
// });
<commit_msg>Remove console clearing from webpack mix (#205)<commit_after>/*
 | Mix Asset Management
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your application.
 |
 | Docs: https://github.com/JeffreyWay/laravel-mix/tree/master/docs#readme
 */

let mix = require("laravel-mix");

mix
  // JS entry file. Supports Vue, and uses Babel
  //
  // More info and options (like React support) here:
  // https://github.com/JeffreyWay/laravel-mix/blob/master/docs/mixjs.md
  .js("static/js/app.js", "public/js")
  // SASS entry file. Uses autoprefixer automatically.
  .sass("static/css/app.scss", "public/css")
  // Customize postCSS:
  // https://github.com/JeffreyWay/laravel-mix/blob/master/docs/css-preprocessors.md#postcss-plugins
  .options({
    // http://lostgrid.org Simple and powerful CSS grids
    postCss: [require("lost")],
    // If you want to process images, change this to true and add options from
    // https://github.com/tcoopman/image-webpack-loader
    imgLoaderOptions: { enabled: false },
    // Stops Mix from clearing the console when compilation succeeds
    clearConsole: false
  })
  // Set public path so manifest gets output here
  .setPublicPath("public")
  // Copies static assets like images and fonts
  .copyDirectory("static/assets", "public/assets")
  // Add assets to the manifest
  .version(["public/assets"])
  // Silence most output from webpack
  .webpackConfig({ stats: "errors-only" })
  .disableSuccessNotifications();

// Full API
// Docs: https://github.com/JeffreyWay/laravel-mix/tree/master/docs#readme
//
// mix.js(src, output);
// mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
// mix.preact(src, output); <-- Identical to mix.js(), but registers Preact compilation.
// mix.coffee(src, output); <-- Identical to mix.js(), but registers CoffeeScript compilation.
// mix.ts(src, output); <-- TypeScript support. Requires tsconfig.json to exist in the same folder as webpack.mix.js
// mix.extract(vendorLibs);
// mix.sass(src, output);
// mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack.
// mix.fastSass('src', output); <-- Alias for mix.standaloneSass().
// mix.less(src, output);
// mix.stylus(src, output);
// mix.postCss(src, output, [require('postcss-some-plugin')()]);
// mix.browserSync('my-site.test');
// mix.combine(files, destination);
// mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
// mix.copy(from, to);
// mix.copyDirectory(fromDir, toDir);
// mix.minify(file);
// mix.sourceMaps(); // Enable sourcemaps
// mix.version(); // Enable versioning.
// mix.disableNotifications();
// mix.disableSuccessNotifications();
// mix.setPublicPath('path/to/public');
// mix.setResourceRoot('prefix/for/resource/locators');
// mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
// mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
// mix.babelConfig({}); <-- Merge extra Babel configuration (plugins, etc.) with Mix's default.
// mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
// mix.extend(name, handler) <-- Extend Mix's API with your own components.
// mix.options({
//   extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
//   globalVueStyles: file, // Variables file to be imported in every component.
//   processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
//   purifyCss: false, // Remove unused CSS selectors.
//   uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
//   postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
// });
<|endoftext|>


<commit_before>(function() {
  const PREFIX = 'octotree'
      , TOKEN  = 'octotree.github_access_token'
      , SHOWN  = 'octotree.shown'
      , REGEXP = /([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/ // (username)/(reponame)/(subpart)
      , RESERVED_USER_NAMES = [
          'settings', 'orgs', 'organizations',
          'site', 'blog', 'about',
          'styleguide', 'showcases', 'trending',
          'stars', 'dashboard', 'notifications'
        ]
      , RESERVED_REPO_NAMES = ['followers', 'following']

  var $html    = $('html')
    , $sidebar = $('' +
                     '
loading...
' +
                     '

' +
                   '')
    , $treeView = $sidebar.find('.octotree_treeview')
    , $tokenFrm = $('<form>' +
                     '

' +
                     '
' +
                       '<input name="token" type="text" placeholder="Paste access token here"></input>' +
                     '
' +
                     '
' +
                       '<button type="submit" class="button">Save</button>' +
                       'Why need access token?' +
                     '
' +
                     '

' +
                   '</form>')
    , $toggleBtn = $('')
    , $dummyDiv  = $('
')
    , store      = new Storage()
    , domInitialized = false
    , currentRepo    = false

  $(document).ready(function() {
    loadRepo()

    // When navigating from non-code pages (i.e. Pulls, Issues) to code page
    // GitHub doesn't reload the page but uses pjax. Need to detect and load Octotree.
    var href = location.href
      , hash = location.hash
    function detectLocationChange() {
      if (location.href !== href || location.hash != hash) {
        href = location.href
        hash = location.hash
        loadRepo()
      }
      window.setTimeout(detectLocationChange, 200)
    }
    detectLocationChange()
  })

  function loadRepo(reload) {
    var repo = getRepoFromPath()
      , repoChanged = JSON.stringify(repo) !== JSON.stringify(currentRepo)

    if (repo && (repoChanged || reload)) {
      initializeDom()
      currentRepo = repo
      fetchData(repo, function(err, tree) {
        if (err) return onFetchError(err)
        renderTree(repo, tree)
      })
    }
  }

  function initializeDom() {
    if (!domInitialized) {
      $('body')
        .append($sidebar)
        .append($toggleBtn.click(toggleSidebar))
      key('⌘+b, ctrl+b', toggleSidebar)
      domInitialized = true
    }
  }

  function getRepoFromPath() {
    // 404 page, skip
    if ($('#parallax_wrapper').length) return false

    var match = location.pathname.match(REGEXP)
    if (!match) return false

    // Not a repository, skip
    if (~RESERVED_USER_NAMES.indexOf(match[1])) return false
    if (~RESERVED_REPO_NAMES.indexOf(match[2])) return false

    // Not a code page, skip
    if (match[3] && !~['tree', 'blob'].indexOf(match[3])) return false

    var branch = $('*[data-master-branch]').data('ref') ||
                 $('*[data-master-branch] > .js-select-button').text() ||
                 'master'
    return {
      username : match[1],
      reponame : match[2],
      branch   : branch
    }
  }

  function fetchData(repo, done) {
    var github  = new Github({ token: store.get(TOKEN) })
      , api     = github.getRepo(repo.username, repo.reponame)
      , root    = []
      , folders = { '': root }

    api.getTree(encodeURIComponent(repo.branch) + '?recursive=true', function(err, tree) {
      if (err) return done(err)
      tree.forEach(function(item) {
        var path   = item.path
          , type   = item.type
          , index  = path.lastIndexOf('/')
          , name   = path.substring(index + 1)
          , folder = folders[path.substring(0, index)]
          , url    = '/' + repo.username + '/' + repo.reponame + '/' + type + '/' + repo.branch + '/' + path

        folder.push(item)
        item.text = sanitize(name)
        item.icon = type // use `type` as class name for tree node
        if (type === 'tree') {
          folders[item.path] = item.children = []
          item.a_attr = { href: '#' }
        }
        else if (type === 'blob') {
          item.a_attr = { href: url }
        }
        // TOOD: handle submodule, anyone?
      })

      done(null, sort(root))

      function sort(folder) {
        folder.sort(function(a, b) {
          if (a.type === b.type) return a.text.localeCompare(b.text)
          return a.type === 'tree' ? -1 : 1
        })
        folder.forEach(function(item) {
          if (item.type === 'tree') sort(item.children)
        })
        return folder
      }
    })
  }

  function onFetchError(err) {
    var header = 'Error: ' + err.error
      , hasToken = !!store.get(TOKEN)
      , message

    if (err.error === 401) {
      header  = 'Invalid token!'
      message = 'The token is invalid. Follow this link to create a new token and paste it in the textbox below.'
    }

    else if (err.error === 404) {
      header = 'Private or invalid repository!'
      if (hasToken) message = 'You are not allowed to access this repository.'
      else          message = 'Accessing private repositories requires a GitHub access token. Follow this link to create one and paste it in the textbox below.'
    }

    else if (err.error === 403 && ~err.request.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
      header = 'API limit exceeded!'
      if (hasToken) message = 'Whoa, you have exceeded the API hourly limit, please create a new access token or take a break :).'
      else          message = 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow this link to create one and paste it in the textbox below.'
    }

    else if (err.error === 409) {
      header  = 'Empty repository!'
      message = 'This repository is empty.'
    }

    updateSidebar('
' + header + '
', message)
  }

  function renderTree(repo, tree) {
    $treeView
      .empty()
      .jstree({
        core    : { data: tree, animation: 100, themes : { responsive : false } },
        plugins : ['wholerow', 'state'],
        state   : { key : PREFIX + '.' + repo.username + '/' + repo.reponame }
      })
      .delegate('.jstree-open>a', 'click.jstree', function() {
        $.jstree.reference(this).close_node(this)
      })
      .delegate('.jstree-closed>a', 'click.jstree', function() {
        $.jstree.reference(this).open_node(this)
      })
      .on('click', function(e) {
        var $target = $(e.target)
        if ($target.is('a.jstree-anchor') && $target.children(':first').hasClass('blob')) {
          $.pjax({
            url       : $target.attr('href'),
            timeout   : 5000, //gives it more time, should really have a progress indicator...
            container : $('#js-repo-pjax-container')
          })
        }
      })
      .on('ready.jstree', function() {
        var headerText = '
' +
                           repo.username + ' / ' + repo.reponame +
                         '
' +
                         '
' +
                           repo.branch +
                         '
'
        updateSidebar(headerText)
      })
  }

  function updateSidebar(header, message) {
    $sidebar.find('.octotree_header').html(header)

    if (message) {
      var token = store.get(TOKEN)
      if (token) $tokenFrm.find('[name="token"]').val(token)
      $tokenFrm.find('.message').html(message)
      $treeView.empty().append($tokenFrm.submit(saveToken))
    }

    // Shows sidebar when:
    // 1. First time after extension is installed
    // 2. If it was previously shown (TODO: many seem not to like it)
    if (store.get(SHOWN) !== false) {
      $html.addClass(PREFIX)
      store.set(SHOWN, true)
    }
  }

  function toggleSidebar() {
    var shown = store.get(SHOWN)
    if (shown) $html.removeClass(PREFIX)
    else $html.addClass(PREFIX)
    store.set(SHOWN, !shown)
  }

  function saveToken(event) {
    event.preventDefault()

    var token  = $tokenFrm.find('[name="token"]').val()
      , $error = $tokenFrm.find('.error').text('')

    if (!token) return $error.text('Token is required')

    store.set(TOKEN, token)
    loadRepo(true)
  }

  function sanitize(str) {
    return $dummyDiv.text(str).html()
  }

  function Storage() {
    this.get = function(key) {
      var val = localStorage.getItem(key)
      try {
        return JSON.parse(val)
      } catch (e) {
        return val
      }
    }
    this.set = function(key, val) {
      return localStorage.setItem(key, JSON.stringify(val))
    }
  }
})()<commit_msg>Sync 2-way octotree and github selection, close #56, #59<commit_after>(function() {
  const PREFIX = 'octotree'
      , TOKEN  = 'octotree.github_access_token'
      , SHOWN  = 'octotree.shown'
      , RESERVED_USER_NAMES = [
          'settings', 'orgs', 'organizations',
          'site', 'blog', 'about',
          'styleguide', 'showcases', 'trending',
          'stars', 'dashboard', 'notifications'
        ]
      , RESERVED_REPO_NAMES = ['followers', 'following']

  var $html    = $('html')
    , $sidebar = $('' +
                     '
loading...
' +
                     '

' +
                   '')
    , $treeView = $sidebar.find('.octotree_treeview')
    , $tokenFrm = $('<form>' +
                     '

' +
                     '
' +
                       '<input name="token" type="text" placeholder="Paste access token here"></input>' +
                     '
' +
                     '
' +
                       '<button type="submit" class="button">Save</button>' +
                       'Why need access token?' +
                     '
' +
                     '

' +
                   '</form>')
    , $toggleBtn = $('')
    , $dummyDiv  = $('
')
    , store      = new Storage()
    , domInitialized = false
    , currentRepo    = false

  $(document).ready(function() {
    loadRepo()

    // When navigating from non-code pages (i.e. Pulls, Issues) to code page
    // GitHub doesn't reload the page but uses pjax. Need to detect and load Octotree.
    var href = location.href
      , hash = location.hash
    function detectLocationChange() {
      if (location.href !== href || location.hash != hash) {
        href = location.href
        hash = location.hash
        loadRepo()
      }
      window.setTimeout(detectLocationChange, 200)
    }
    detectLocationChange()
  })

  function loadRepo(reload) {
    var repo = getRepoFromPath()
      , repoChanged = JSON.stringify(repo) !== JSON.stringify(currentRepo)

    if (repo && (repoChanged || reload)) {
      initializeDom()
      currentRepo = repo
      fetchData(repo, function(err, tree) {
        if (err) return onFetchError(err)
        renderTree(repo, tree, selectTreeNode)

      })
    } else selectTreeNode()
  }

  function selectTreeNode() {
    var tree = $.jstree.reference($treeView)
      , path = location.pathname

    tree.deselect_all()

    // e.g. converts /buunguyen/octotree/type/branch/path to path
    var match = path.match(/(?:[^\/]+\/){4}(.*)/)
    if (match) tree.select_node(nodeIdFromPath(decodeURIComponent(match[1])))
  }

  function initializeDom() {
    if (!domInitialized) {
      $('body')
        .append($sidebar)
        .append($toggleBtn.click(toggleSidebar))

      key('⌘+b, ctrl+b', toggleSidebar)
      domInitialized = true
    }
  }

  function getRepoFromPath() {
    // 404 page, skip
    if ($('#parallax_wrapper').length) return false

    // (username)/(reponame)[/(subpart)]
    var match = location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
    if (!match) return false

    // Not a repository, skip
    if (~RESERVED_USER_NAMES.indexOf(match[1])) return false
    if (~RESERVED_REPO_NAMES.indexOf(match[2])) return false

    // Not a code page, skip
    if (match[3] && !~['tree', 'blob'].indexOf(match[3])) return false

    var branch = $('*[data-master-branch]').data('ref') ||
                 $('*[data-master-branch] > .js-select-button').text() ||
                 'master'
    return {
      username : match[1],
      reponame : match[2],
      branch   : branch
    }
  }

  function fetchData(repo, done) {
    var github  = new Github({ token: store.get(TOKEN) })
      , api     = github.getRepo(repo.username, repo.reponame)
      , root    = []
      , folders = { '': root }

    api.getTree(encodeURIComponent(repo.branch) + '?recursive=true', function(err, tree) {
      if (err) return done(err)
      tree.forEach(function(item) {
        var path   = item.path
          , type   = item.type
          , index  = path.lastIndexOf('/')
          , name   = path.substring(index + 1)
          , folder = folders[path.substring(0, index)]
          , url    = '/' + repo.username + '/' + repo.reponame + '/' + type + '/' + repo.branch + '/' + path

        folder.push(item)
        item.id   = nodeIdFromPath(path)
        item.text = sanitize(name)
        item.icon = type // use `type` as class name for tree node
        if (type === 'tree') {
          folders[item.path] = item.children = []
          item.a_attr = { href: '#' }
        }
        else if (type === 'blob') {
          item.a_attr = { href: url }
        }
      })

      done(null, sort(root))

      function sort(folder) {
        folder.sort(function(a, b) {
          if (a.type === b.type) return a.text.localeCompare(b.text)
          return a.type === 'tree' ? -1 : 1
        })
        folder.forEach(function(item) {
          if (item.type === 'tree') sort(item.children)
        })
        return folder
      }
    })
  }

  function onFetchError(err) {
    var header = 'Error: ' + err.error
      , hasToken = !!store.get(TOKEN)
      , message

    if (err.error === 401) {
      header  = 'Invalid token!'
      message = 'The token is invalid. Follow this link to create a new token and paste it in the textbox below.'
    }

    else if (err.error === 404) {
      header = 'Private or invalid repository!'
      if (hasToken) message = 'You are not allowed to access this repository.'
      else          message = 'Accessing private repositories requires a GitHub access token. Follow this link to create one and paste it in the textbox below.'
    }

    else if (err.error === 403 && ~err.request.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
      header = 'API limit exceeded!'
      if (hasToken) message = 'Whoa, you have exceeded the API hourly limit, please create a new access token or take a break :).'
      else          message = 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow this link to create one and paste it in the textbox below.'
    }

    else if (err.error === 409) {
      header  = 'Empty repository!'
      message = 'This repository is empty.'
    }

    updateSidebar('
' + header + '
', message)
  }

  function renderTree(repo, tree, cb) {
    $treeView
      .empty()
      .jstree({
        core    : { data: tree, animation: 100, themes : { responsive : false } },
        plugins : ['wholerow', 'state'],
        state   : { key : PREFIX + '.' + repo.username + '/' + repo.reponame }
      })
      .on('click.jstree', '.jstree-open>a', function() {
        $.jstree.reference(this).close_node(this)
      })
      .on('click.jstree', '.jstree-closed>a', function() {
        $.jstree.reference(this).open_node(this)
      })
      .on('click', function(e) {
        var $target = $(e.target)
        if ($target.is('a.jstree-anchor') && $target.children(':first').hasClass('blob')) {
          $.pjax({
            url       : $target.attr('href'),
            timeout   : 5000, //gives it more time, should really have a progress indicator...
            container : $('#js-repo-pjax-container')
          })
        }
      })
      .on('ready.jstree', function() {
        var headerText = '
' +
                           repo.username + ' / ' + repo.reponame +
                         '
' +
                         '
' +
                           repo.branch +
                         '
'
        updateSidebar(headerText)
        cb()
      })
  }

  function updateSidebar(header, message) {
    $sidebar.find('.octotree_header').html(header)

    if (message) {
      var token = store.get(TOKEN)
      if (token) $tokenFrm.find('[name="token"]').val(token)
      $tokenFrm.find('.message').html(message)
      $treeView.empty().append($tokenFrm.submit(saveToken))
    }

    // Shows sidebar when:
    // 1. First time after extension is installed
    // 2. If it was previously shown (TODO: many seem not to like it)
    if (store.get(SHOWN) !== false) {
      $html.addClass(PREFIX)
      store.set(SHOWN, true)
    }
  }

  function toggleSidebar() {
    var shown = store.get(SHOWN)
    if (shown) $html.removeClass(PREFIX)
    else $html.addClass(PREFIX)
    store.set(SHOWN, !shown)
  }

  function saveToken(event) {
    event.preventDefault()

    var token  = $tokenFrm.find('[name="token"]').val()
      , $error = $tokenFrm.find('.error').text('')

    if (!token) return $error.text('Token is required')

    store.set(TOKEN, token)
    loadRepo(true)
  }

  function sanitize(str) {
    return $dummyDiv.text(str).html()
  }

  // jstree messes up badly when node ID contains $, so need to handle it
  // TODO: check if there's other character that breaks jstree
  function nodeIdFromPath(path) {
    return PREFIX + path.replace(/\$/g, '_')
  }

  function Storage() {
    this.get = function(key) {
      var val = localStorage.getItem(key)
      try {
        return JSON.parse(val)
      } catch (e) {
        return val
      }
    }
    this.set = function(key, val) {
      return localStorage.setItem(key, JSON.stringify(val))
    }
  }
})()<|endoftext|>


<commit_before>/**
 * ffmpeg video conversion
 *
 * MIT Licensed, see License.txt
 *
 * https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
 *
 */

// utilities module
var utilities = require('./bower_components/chrisweb-utilities/utilities');

var Fluent = require('fluent-ffmpeg');

var fs = require('fs');

var path = require('path');

utilities.log('[VIDEO CONVERTOR] starting');

// windows only
if (typeof(process.env.FFMPEG_PATH) === 'undefined') {

    process.env.FFMPEG_PATH = 'C:\ffmpeg\bin\ffmpeg.exe';

    utilities.log('FFMPEG_PATH not found, setting it by default to "' + process.env.FFMPEG_PATH.toUpperCase() + '"', 'fontColor:red');

}

if (typeof(process.env.FFPROBE_PATH) === 'undefined') {

    process.env.FFPROBE_PATH = 'C:\ffmpeg\bin\ffprobe.exe';

    utilities.log('FFPROBE_PATH not found, setting it by default to "' + process.env.FFPROBE_PATH.toUpperCase() + '"', 'fontColor:red');

}

var videoDirectory = './videos';

var files = fs.readdirSync(videoDirectory);

var fluentCommand = new Fluent();

for (var i in files) {

    if (path.extname(files[i]) === '.mov') {

        var videoSource = videoDirectory + '/' + files[i];

        var videoOutputWebm = videoDirectory + '/hompage-video_' + i + '.webm';
        var videoOutputMpeg = videoDirectory + '/hompage-video_' + i + '.mp4';

        var fluentCommand = new Fluent();

        // video input
        fluentCommand.input(videoSource)
                    // specify input format
                    .inputFormat('mov')
                    // disable audio as we dont need it for the intro videos
                    .noAudio();
                    // specify output frame rate
                    //.fps(29.7)

        // second output as webm
        fluentCommand.outputFormat('webm') // set output format
                    .output(videoOutputWebm)
                    // specify output frame rate
                    //.fps(29.7)

        // first ouput as mpeg
        fluentCommand.outputFormat('mp4') // set output format
                    .output(videoOutputMpeg)
                    // specify output frame rate
                    //.fps(29.7)

        fluentCommand.on('error', function(err) {
            console.log('An error occurred: ' + err.message);
        });

        fluentCommand.on('end', function() {
            console.log('Processing finished !');
        });

        fluentCommand.run();

    }

}<commit_msg>logging details<commit_after>/**
 * ffmpeg video conversion
 *
 * MIT Licensed, see License.txt
 *
 * https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
 *
 */

// utilities module
var utilities = require('./bower_components/chrisweb-utilities/utilities');

var Fluent = require('fluent-ffmpeg');

var fs = require('fs');

var path = require('path');

utilities.log('[VIDEO CONVERTOR] starting');

// windows only
if (typeof(process.env.FFMPEG_PATH) === 'undefined') {

    process.env.FFMPEG_PATH = 'C:\ffmpeg\bin\ffmpeg.exe';

    utilities.log('FFMPEG_PATH not found, setting it by default to "' + process.env.FFMPEG_PATH.toUpperCase() + '"', 'fontColor:red');

}

if (typeof(process.env.FFPROBE_PATH) === 'undefined') {

    process.env.FFPROBE_PATH = 'C:\ffmpeg\bin\ffprobe.exe';

    utilities.log('FFPROBE_PATH not found, setting it by default to "' + process.env.FFPROBE_PATH.toUpperCase() + '"', 'fontColor:red');

}

var videoDirectory = './videos';

var files = fs.readdirSync(videoDirectory);

var fluentCommand = new Fluent();

for (var i in files) {

    if (path.extname(files[i]) === '.mov') {

        var videoSource = videoDirectory + '/' + files[i];

        var videoOutputWebm = videoDirectory + '/hompage-video_' + i + '.webm';
        var videoOutputMpeg = videoDirectory + '/hompage-video_' + i + '.mp4';

        var fluentCommand = new Fluent();

        // video input
        fluentCommand.input(videoSource)
                    // specify input format
                    .inputFormat('mov')
                    // disable audio as we dont need it for the intro videos
                    .noAudio();
                    // specify output frame rate
                    //.fps(29.7)

        // second output as webm
        fluentCommand.outputFormat('webm') // set output format
                    .output(videoOutputWebm)
                    // specify output frame rate
                    //.fps(29.7)

        // first ouput as mpeg
        fluentCommand.outputFormat('mp4') // set output format
                    .output(videoOutputMpeg)
                    // specify output frame rate
                    //.fps(29.7)

        fluentCommand.on('start', function(commandLine) {
            utilities.log('Spawned Ffmpeg with command: ' + commandLine, 'fontColor:green');
        });

        fluentCommand.on('codecData', function(data) {

            utilities.log('codecData:');
            utilities.log('codecData:');

            utilities.log('format: ' + data.format);
            utilities.log('duration: ' + data.duration);
            utilities.log('audio: ' + data.audio);
            utilities.log('audio_details: ' + data.audio_details);
            utilities.log('video: ' + data.video);
            utilities.log('video_details: ' + data.video_details);

        });

        fluentCommand.on('progress', function(progress) {

            utilities.log('Processing: ' + progress.percent + '% done');

        });

        fluentCommand.on('error', function(err) {
            utilities.log('An error occurred: ' + err.message, 'fontColor:red');
        });

        fluentCommand.on('end', function() {
            utilities.log('Processing finished !');
        });

        fluentCommand.run();

    }

}<|endoftext|>


<commit_before>"use strict";

const fs        = require ('fs')
const ccxt      = require ('./ccxt')
const countries = require ('./countries')
const asTable   = require ('as-table')
const util      = require ('util')

let markets
let verbose = false

try {

    markets = require ('./config')

} catch (e) {

    markets = { // defaults

        _1broker:    { 'verbose': verbose, apiKey: '', token: '', },
        _1btcxe:     { 'verbose': verbose, apiKey: '', secret: '', },
        anxpro:      { 'verbose': verbose, apiKey: '', secret: '', },
        bit2c:       { 'verbose': verbose, apiKey: '', secret: '', },
        bitbay:      { 'verbose': verbose, apiKey: '', secret: '', },
        bitbays:     { 'verbose': verbose, apiKey: '', secret: '', },
        bitcoincoid: { 'verbose': verbose, apiKey: '', secret: '', },
        bitfinex:    { 'verbose': verbose, apiKey: '', secret: '', },
        bitlish:     { 'verbose': verbose, apiKey: '', login: '', password: '', },
        bitmarket:   { 'verbose': verbose, apiKey: '', secret: '', },
        bitmex:      { 'verbose': verbose, apiKey: '', secret: '', },
        bitso:       { 'verbose': verbose, apiKey: '', secret: '', },
        bitstamp:    { 'verbose': verbose, apiKey: '', secret: '', uid: '', },
        bittrex:     { 'verbose': verbose, apiKey: '', secret: '', },
        btcchina:    { 'verbose': verbose, apiKey: '', secret: '', },
        btcx:        { 'verbose': verbose, apiKey: '', secret: '', },
        bxinth:      { 'verbose': verbose, apiKey: '', secret: '', },
        ccex:        { 'verbose': verbose, apiKey: '', secret: '', },
        cex:         { 'verbose': verbose, apiKey: '', secret: '', uid: '', },
        coincheck:   { 'verbose': verbose, apiKey: '', secret: '', },
        coinsecure:  { 'verbose': verbose, apiKey: '', },
        exmo:        { 'verbose': verbose, apiKey: '', secret: '', },
        fybse:       { 'verbose': verbose, apiKey: '', secret: '', },
        fybsg:       { 'verbose': verbose, apiKey: '', secret: '', },
        gdax:        { 'verbose': verbose, apiKey: '', secret: '', password: '' },
        hitbtc:      { 'verbose': verbose, apiKey: '', secret: '', },
        huobi:       { 'verbose': verbose, apiKey: '', secret: '', },
        jubi:        { 'verbose': verbose, apiKey: '', secret: '', },
        kraken:      { 'verbose': verbose, apiKey: '', secret: '', },
        luno:        { 'verbose': verbose, apiKey: '', secret: '', },
        okcoinusd:   { 'verbose': verbose, apiKey: '', secret: '', },
        okcoincny:   { 'verbose': verbose, apiKey: '', secret: '', },
        paymium:     { 'verbose': verbose, apiKey: '', secret: '', },
        poloniex:    { 'verbose': verbose, apiKey: '', secret: '', },
        quadrigacx:  { 'verbose': verbose, apiKey: '', secret: '', uid: 123, },
        quoine:      { 'verbose': verbose, apiKey: '', secret: '', },
        therock:     { 'verbose': verbose, apiKey: '', secret: '', },
        vaultoro:    { 'verbose': verbose, apiKey: '', secret: '', },
        virwox:      { 'verbose': verbose, apiKey: '', login: '', password: '', },
        yobit:       { 'verbose': verbose, apiKey: '', secret: '', },
        zaif:        { 'verbose': verbose, apiKey: '', secret: '', },
    }
}

for (let id in markets) {
    markets[id] = new (ccxt)[id] (markets[id])
    markets[id].verbose = verbose
}

// console.log (Object.values (ccxt).length)

var countryName = function (code) {
    return ((typeof countries[code] !== 'undefined') ? countries[code] : code)
}

let sleep = async ms => await new Promise (resolve => setTimeout (resolve, ms))

//-------------------------------------------------------------------------
// list all supported exchanges

let values = Object.values (markets).map (market => {
    let logo = market.urls['logo']
    let website = Array.isArray (market.urls.www) ? market.urls.www[0] : market.urls.www
    let countries = Array.isArray (market.countries) ? market.countries.map (countryName).join (', ') : countryName (market.countries)
    let doc = Array.isArray (market.urls.doc) ? market.urls.doc[0] : market.urls.doc
    let version = market.version ? market.version : '\*'
    if (version[0] == 'v')
        version = version.slice (1)
    return {
        '': '![' + market.id + '](' + logo + ')',
        'id': market.id,
        'name': '[' + market.name + '](' + website + ')',
        'ver': version,
        'doc': '[API](' + doc + ')',
        'countries': countries,
    }
})

let numMarkets = Object.keys (markets).length
let exchanges = asTable.configure ({ delimiter: ' | ' }) (values)
let lines = exchanges.split ("\n")
lines[1] = lines[0].replace (/[^\|]/g, '-')
let headerLine = lines[1].split ('|')
headerLine[3] = ':' + headerLine[3].slice (1, headerLine[3].length - 1) + ':'
headerLine[4] = ':' + headerLine[4].slice (1, headerLine[4].length - 1) + ':'
lines[1] = headerLine.join ('|')

lines = lines.map (line => '|' + line + '|').join ("\n")

let changeInFile = (file) => {
    // console.log (file)
    let oldContent = fs.readFileSync (file, 'utf8')
    let beginning = "The ccxt library currently supports the following "
    let ending = " cryptocurrency exchange markets and trading APIs:\n\n"
    let regex = new RegExp ("[^\n]+[\n][\n]\\|[^#]+\\|([\n][\n]|[\n]$|$)", 'm')
    let totalString = beginning + numMarkets + ending
    let replacement = totalString + lines + "$1"
    let newContent = oldContent.replace (regex, replacement)
    // console.log (newContent)
    fs.writeFileSync (file, newContent)
}

changeInFile ('README.md')
changeInFile ('../ccxt.wiki/Exchange-Markets.md')
changeInFile ('../ccxt.wiki/Manual.md')

console.log ('Markets exported successfully.')
<commit_msg>added a list of exchanges by country<commit_after>"use strict";

const fs        = require ('fs')
const ccxt      = require ('./ccxt')
const countries = require ('./countries')
const asTable   = require ('as-table')
const util      = require ('util')

let markets
let verbose = false

try {

    markets = require ('./config')

} catch (e) {

    markets = { // defaults

        _1broker:    { 'verbose': verbose, apiKey: '', token: '', },
        _1btcxe:     { 'verbose': verbose, apiKey: '', secret: '', },
        anxpro:      { 'verbose': verbose, apiKey: '', secret: '', },
        bit2c:       { 'verbose': verbose, apiKey: '', secret: '', },
        bitbay:      { 'verbose': verbose, apiKey: '', secret: '', },
        bitbays:     { 'verbose': verbose, apiKey: '', secret: '', },
        bitcoincoid: { 'verbose': verbose, apiKey: '', secret: '', },
        bitfinex:    { 'verbose': verbose, apiKey: '', secret: '', },
        bitlish:     { 'verbose': verbose, apiKey: '', login: '', password: '', },
        bitmarket:   { 'verbose': verbose, apiKey: '', secret: '', },
        bitmex:      { 'verbose': verbose, apiKey: '', secret: '', },
        bitso:       { 'verbose': verbose, apiKey: '', secret: '', },
        bitstamp:    { 'verbose': verbose, apiKey: '', secret: '', uid: '', },
        bittrex:     { 'verbose': verbose, apiKey: '', secret: '', },
        btcchina:    { 'verbose': verbose, apiKey: '', secret: '', },
        btcx:        { 'verbose': verbose, apiKey: '', secret: '', },
        bxinth:      { 'verbose': verbose, apiKey: '', secret: '', },
        ccex:        { 'verbose': verbose, apiKey: '', secret: '', },
        cex:         { 'verbose': verbose, apiKey: '', secret: '', uid: '', },
        coincheck:   { 'verbose': verbose, apiKey: '', secret: '', },
        coinsecure:  { 'verbose': verbose, apiKey: '', },
        exmo:        { 'verbose': verbose, apiKey: '', secret: '', },
        fybse:       { 'verbose': verbose, apiKey: '', secret: '', },
        fybsg:       { 'verbose': verbose, apiKey: '', secret: '', },
        gdax:        { 'verbose': verbose, apiKey: '', secret: '', password: '' },
        hitbtc:      { 'verbose': verbose, apiKey: '', secret: '', },
        huobi:       { 'verbose': verbose, apiKey: '', secret: '', },
        jubi:        { 'verbose': verbose, apiKey: '', secret: '', },
        kraken:      { 'verbose': verbose, apiKey: '', secret: '', },
        luno:        { 'verbose': verbose, apiKey: '', secret: '', },
        okcoinusd:   { 'verbose': verbose, apiKey: '', secret: '', },
        okcoincny:   { 'verbose': verbose, apiKey: '', secret: '', },
        paymium:     { 'verbose': verbose, apiKey: '', secret: '', },
        poloniex:    { 'verbose': verbose, apiKey: '', secret: '', },
        quadrigacx:  { 'verbose': verbose, apiKey: '', secret: '', uid: 123, },
        quoine:      { 'verbose': verbose, apiKey: '', secret: '', },
        therock:     { 'verbose': verbose, apiKey: '', secret: '', },
        vaultoro:    { 'verbose': verbose, apiKey: '', secret: '', },
        virwox:      { 'verbose': verbose, apiKey: '', login: '', password: '', },
        yobit:       { 'verbose': verbose, apiKey: '', secret: '', },
        zaif:        { 'verbose': verbose, apiKey: '', secret: '', },
    }
}

for (let id in markets) {
    markets[id] = new (ccxt)[id] (markets[id])
    markets[id].verbose = verbose
}

// console.log (Object.values (ccxt).length)

var countryName = function (code) {
    return ((typeof countries[code] !== 'undefined') ? countries[code] : code)
}

let sleep = async ms => await new Promise (resolve => setTimeout (resolve, ms))

//-------------------------------------------------------------------------
// list all supported exchanges

let values = Object.values (markets).map (market => {
    let logo = market.urls['logo']
    let website = Array.isArray (market.urls.www) ? market.urls.www[0] : market.urls.www
    let countries = Array.isArray (market.countries) ? market.countries.map (countryName).join (', ') : countryName (market.countries)
    let doc = Array.isArray (market.urls.doc) ? market.urls.doc[0] : market.urls.doc
    let version = market.version ? market.version : '\*'
    if (version[0] == 'v')
        version = version.slice (1)
    return {
        '': '![' + market.id + '](' + logo + ')',
        'id': market.id,
        'name': '[' + market.name + '](' + website + ')',
        'ver': version,
        'doc': '[API](' + doc + ')',
        'countries': countries,
    }
})

let numMarkets = Object.keys (markets).length
let exchanges = asTable.configure ({ delimiter: ' | ' }) (values)
let lines = exchanges.split ("\n")
lines[1] = lines[0].replace (/[^\|]/g, '-')
let headerLine = lines[1].split ('|')
headerLine[3] = ':' + headerLine[3].slice (1, headerLine[3].length - 1) + ':'
headerLine[4] = ':' + headerLine[4].slice (1, headerLine[4].length - 1) + ':'
lines[1] = headerLine.join ('|')

lines = lines.map (line => '|' + line + '|').join ("\n")

let changeInFile = (filename) => {
    console.log (filename)
    let oldContent = fs.readFileSync (filename, 'utf8')
    let beginning = "The ccxt library currently supports the following "
    let ending = " cryptocurrency exchange markets and trading APIs:\n\n"
    let regex = new RegExp ("[^\n]+[\n][\n]\\|[^#]+\\|([\n][\n]|[\n]$|$)", 'm')
    let totalString = beginning + numMarkets + ending
    let replacement = totalString + lines + "$1"
    let newContent = oldContent.replace (regex, replacement)
    // console.log (newContent)
    fs.writeFileSync (filename, newContent)
}

changeInFile ('README.md')
changeInFile ('../ccxt.wiki/Exchange-Markets.md')
changeInFile ('../ccxt.wiki/Manual.md')

console.log (typeof countries)
console.log (countries)

let marketsByCountries = []
Object.keys (countries).forEach (code => {
    let country = countries[code]
    let result = []
    Object.keys (markets).forEach (id => {
        let market = markets[id]
        let logo = market.urls['logo']
        let website = Array.isArray (market.urls.www) ? market.urls.www[0] : market.urls.www
        let doc = Array.isArray (market.urls.doc) ? market.urls.doc[0] : market.urls.doc
        let version = market.version ? market.version : '\*'
        let shouldInclude = false
        if (Array.isArray (market.countries)) {
            if (market.countries.indexOf (code) > -1)
                shouldInclude = true
        } else {
            if (code == market.countries)
                shouldInclude = true
        }
        if (shouldInclude) {
            result.push ({
                'country / region': country,
                'logo': '![' + market.id + '](' + logo + ')',
                'id': market.id,
                'name': '[' + market.name + '](' + website + ')',
                'ver': version,
                'doc': '[API](' + doc + ')',
            })
        }
    })
    marketsByCountries = marketsByCountries.concat (result)
});

;(() => {
    let exchanges = asTable.configure ({ delimiter: ' | ' }) (marketsByCountries)
    let lines = exchanges.split ("\n")
    lines[1] = lines[0].replace (/[^\|]/g, '-')
    let headerLine = lines[1].split ('|')
    headerLine[4] = ':' + headerLine[4].slice (1, headerLine[4].length - 1) + ':'
    headerLine[5] = ':' + headerLine[5].slice (1, headerLine[5].length - 1) + ':'
    lines[1] = headerLine.join ('|')
    lines = lines.map (line => '|' + line + '|').join ("\n")
    let result = "The ccxt library currently supports the following cryptocurrency exchange markets and trading APIs:\n\n" + lines + "\n\n"
    fs.writeFileSync ('../ccxt.wiki/Exchange-Markets-By-Country.md', result)
    // console.log (result)
}) ();

// console.log (marketsByCountries)
// console.log (asTable.configure ({ delimiter: ' | ' }) (marketsByCountries))

console.log ('Markets exported successfully.')
<|endoftext|>


<commit_before>var express = require('express'),
    // path = require('path'),
    morgan = require('morgan'),
    app = express();

app.use(morgan('dev'));
app.use(express.static(__dirname + '/../app'));

require('./routes.js')(app);

// app.get('/', function(req, res) {
//     res.sendFile(__dirname + '/../app/analyze/game/game.html');
// });

// app.use("/js", express.static(__dirname + "/app/js"));
// app.use("/img", express.static(__dirname + "/app/img"));
// app.use("/css", express.static(__dirname + "/app/css"));
app.all("/*", function(req, res, next) {
        res.sendFile("index.html", { root: __dirname + "/../app" });
});

app.listen(3001);

console.log('Listening on port 3001...');
<commit_msg>modified server.js to use the port from azure (from: http://stackoverflow.com/questions/19294203/node-js-express-only-work-on-port-3000-on-test-server)<commit_after>var express = require('express'),
    // path = require('path'),
    morgan = require('morgan'),
    app = express();

app.use(morgan('dev'));
app.use(express.static(__dirname + '/../app'));

require('./routes.js')(app);

// app.get('/', function(req, res) {
//     res.sendFile(__dirname + '/../app/analyze/game/game.html');
// });

// app.use("/js", express.static(__dirname + "/app/js"));
// app.use("/img", express.static(__dirname + "/app/img"));
// app.use("/css", express.static(__dirname + "/app/css"));
app.all("/*", function(req, res, next) {
        res.sendFile("index.html", { root: __dirname + "/../app" });
});

var port = process.env.port || 3001;

app.listen(port);

console.log('Listening on port ' + port + '...');
<|endoftext|>


<commit_before>/*global define, console */
define(['util/mathUtil', 'util/vector', 'util/car', 'util/road', 'util/consts'],
    function (mathUtil, Vector, Car, Road, consts) {
        'use strict';

        /** @type {number} */
        var deltaT = (1 / consts.TARGET_FPS);
        /** @type {number} */
        var deltaTSquared = deltaT * deltaT;

        /** @type {World} */
        var world;

        /** @type {number} */
        var width;
        /** @type {number} */
        var height;
        /** @type {number} */
        var runCount = 0;

        /** @type {Array.<boolean>} */
        var shouldStop = [];

        /** @type {HTMLCanvasElement} */
        var _canvas;
        /** @type {CanvasRenderingContext2D} */
        var _context;
        /** @type {ImageData} */
        var _backgroundImage;

        /** @type {HTMLElement} */
        var _fpsElem;

        /**
         * @param {number} width
         * @param {number} height
         * @constructor
         * @returns {{width:number, height:number, roads:Array.<Road>}}
         */
        function World(width, height) {
            this.width = width;
            this.height = height;
            this.roads = [];

        }

        /**
         * @param {CanvasRenderingContext2D} context
         * @param {World} world
         */
        function drawWorld(context, world) {
            var i;

            /**
             * @param {Road} road
             * @param {Car} car
             * @param {number} carIndex
             */
            function drawCar(road, car, carIndex) {
                var transform = road.roadToWorldPosition(car.position, car.lane);
                context.save();
                context.translate(transform.translate.x, transform.translate.y);
                context.rotate(Math.PI/2 - Math.atan2(transform.tangent.x, transform.tangent.y));
                car.draw(context);
                context.restore();
                //context.fillText(carIndex, drawPosition.x + 10, drawPosition.y + 10);
            }

            for (i = 0; i < world.roads.length; i += 1) {
                world.roads[i].forEachCar(drawCar);
            }
        }

        /**
         * @param {HTMLCanvasElement} canvas
         * @param {CanvasRenderingContext2D} context
         * @param {World} world
         * @returns {ImageData}
         */
        function drawBackground(canvas, context, world) {
            /** @type {Road} */
            var road;
            /** @type {number} */
            var i;
            for (i = 0; i < world.roads.length; i += 1) {
                road = world.roads[i];
                road.draw(context);
            }
            return context.getImageData(0, 0, canvas.width, canvas.height);
        }

        /**
         * @param {Road} road
         * @param {number} lane
         * @param {number} carIndex
         * @returns {{car:Car,index:number}}
         */
        function getNextCarInLane(road, lane, carIndex) {
            /** @type {number} */
            var i;
            /** @type {number} */
            var index;
            /** @type {Car} */
            var car;

            for (i = 0; i < road.cars.length - 1; i += 1) {
                index = (i + carIndex + 1) % road.cars.length;
                car = road.cars[index];
                if (car.lane === lane) {
                    return {
                        index: index,
                        car: car
                    };
                }
            }
            return null;
        }

        /**
         * @param {Road} road
         * @param {number} lane
         * @param {number} carIndex
         * @returns {{car:Car,index:number}}
         */
        function getPrevCarInLane(road, lane, carIndex) {
            /** @type {number} */
            var i;
            /** @type {number} */
            var index;
            /** @type {Car} */
            var car;

            for (i = 0; i < road.cars.length - 1; i += 1) {
                index = (carIndex - 1 - i + road.cars.length) % road.cars.length;
                car = road.cars[index];
                if (car.lane === lane) {
                    return {
                        index: index,
                        car: car
                    };
                }
            }
            return null;
        }

        /**
         * @param {Road} road
         * @param {number} laneNum
         * @param {number} carIndex
         * @returns {number}
         */
        function spaceAvailableInLane(road, laneNum, carIndex) {
            /** @type {Car} */
            var car = road.cars[carIndex];
            var minSwitchSpace = car.length * (2 + 1.0 * (Math.min(1, car.speed)));
            var nextCarInfo = getNextCarInLane(road, laneNum, carIndex);
            var prevCarInfo = getPrevCarInLane(road, laneNum, carIndex);
            /** @type {number} */
            var forwardSpace;
            /** @type {number} */
            var backwardSpace;

            if (!nextCarInfo || !prevCarInfo) {
                if (!nextCarInfo && !prevCarInfo) {
                    return road.length - car.length;
                }
                return 0;
            }
            forwardSpace = (nextCarInfo.index < carIndex ? -1 : 1) * (nextCarInfo.car.position - car.position) - car.length;
            backwardSpace = (carIndex < prevCarInfo.index ? -1 : 1) * (car.position - prevCarInfo.car.position) - prevCarInfo.car.length;

            if ((forwardSpace < minSwitchSpace) || (backwardSpace < minSwitchSpace)) {
                return 0;
            }
            return forwardSpace;
        }

        /**
         * @param {Road} road
         * @param {Car} car
         * @param {number} carIndex
         * @returns {number}
         */
        function decideAcceleration(road, car, carIndex) {
            var distanceToNextCarBackside;
            var closingSpeed;
            var nextCarRelativePosition;
            var impactTime;
            var keepingTime;
            var nextAccel;
            var nextCar;
            if (car.wrecked) {
                return 0;
            }
            nextCar = getNextCarInLane(road, car.lane, carIndex).car;
            if (!nextCar) {
                return car.maxAcceleration;
            }
            nextAccel = car.maxAcceleration;
            nextCarRelativePosition = nextCar.position + (nextCar.position < car.position ? road.length : 0);
            closingSpeed = car.speed - nextCar.speed;
            distanceToNextCarBackside = nextCarRelativePosition - car.position - car.length;
            if (distanceToNextCarBackside < 0) {
                // Wreck!
                console.log('Collision!', car.color, nextCar.color);
                car.wreck();
                nextCar.wreck();
                //debugger;
                return 0;
            }
            if (closingSpeed > 0) {
                impactTime = distanceToNextCarBackside / closingSpeed;
                if (impactTime < consts.MIN_IMPACT_TIME) {
                    nextAccel = -closingSpeed / (impactTime / 2);
                }
            }
            //else {
            // never going to impact, closing speed is negative (actually gaining distance) or very, very small.
            //}

            keepingTime = distanceToNextCarBackside / car.speed;
            if (keepingTime < car.minKeepingTime) {
                // too close for comfort
                nextAccel = Math.min(nextAccel, -car.speed / keepingTime);
            }

            if (distanceToNextCarBackside < consts.MIN_KEEPING_DISTANCE) {
                nextAccel = Math.min(nextAccel, distanceToNextCarBackside - consts.MIN_KEEPING_DISTANCE);
            }

            //console.log('Car:', car.color, 'Distance:', distanceToNextCarBackside, 'Closing speed:', closingSpeed, 'Impact time:', impactTime, 'Accel: ', car.accel);
            if (nextAccel > 0) {
                nextAccel = Math.min(nextAccel, car.maxAcceleration);
            }
            return nextAccel;
        }

        /**
         * @param {Road} road
         * @param {Car} car
         * @param {number} carIndex
         * @returns {number}
         */
        function decideNextLane(road, car, carIndex) {
            var spaceInCurrentLane;
            var spaceInOtherLane;
            var nextLane = car.lane;
            if (car.wrecked) {
                return car.lane;
            }
            spaceInCurrentLane = spaceAvailableInLane(road, car.lane, carIndex);
            if (spaceInCurrentLane > car.length * 10) {
                return car.lane; // plenty of space in current lane.
            }
            if (car.lane < road.numLanes - 1) {
                spaceInOtherLane = spaceAvailableInLane(road, car.lane + 1, carIndex);
                if (spaceInOtherLane > spaceInCurrentLane) {
//                    console.log(car.color, 'Switching lanes. Current lane: ', car.lane,
//                        'Position: ', car.position,
//                        'Space in this lane:', spaceInCurrentLane, 'in other lane:', spaceInOtherLane);
                    nextLane = car.lane + 1;
                }
            }
            if (nextLane === car.lane && (car.lane > 0)) {
                spaceInOtherLane = spaceAvailableInLane(road, car.lane - 1, carIndex);
                if (spaceInOtherLane > spaceInCurrentLane) {
//                    console.log(car.color, 'Switching lanes. Current lane: ', car.lane,
//                        'Position: ', car.position,
//                        ' Space in this lane:', spaceInCurrentLane, 'in other lane:', spaceInOtherLane);
                    nextLane = car.lane - 1;
                }
            }
            return nextLane;
        }

        /**
         * @param {Road} road
         * @param {Car} car
         */
        function simulateNewtonMechanics(road, car) {
            car.speed = Math.min(car.maxSpeed, Math.max(0, car.speed + car.accel * deltaT));
            car.position += car.speed * deltaT + 0.5 * car.accel * deltaTSquared;

            while (car.position > road.length) {
                car.position -= road.length;
            }
            while (car.position < 0) {
                car.position += road.length;
            }
        }

        /**
         * @param {Road} road
         * @param {Car} car
         * @param {number} carIndex
         * @returns {Function|null}
         */
        function simulateCar(road, car, carIndex) {
            var nextLane = decideNextLane(road, car, carIndex);
            var nextAccel = decideAcceleration(road, car, carIndex);
            if (car.wrecked) {
                return null;
            }

            car.accel = nextAccel;
            simulateNewtonMechanics(road, car);

            return function () {
                car.lane = nextLane;
            };
        }

        function simulateStep(world) {
            var i, j;
            var carUpdater;
            var carUpdaters;
            /** @type {Road} */
            var road;
            for (i = 0; i < world.roads.length; i += 1) {
                road = world.roads[i];
                carUpdaters = road.forEachCar(simulateCar);
                for (j = 0; j < carUpdaters.length; j += 1) {
                    carUpdater = carUpdaters[j];
                    if (carUpdater) {
                        carUpdater();
                    }
                }
                road.sortCars();
            }
        }

        function stop() {
            shouldStop[runCount] = true;
            runCount += 1;
        }

        function resetCanvas() {
            // Store the current transformation matrix
            _context.save();

            // Use the identity matrix while clearing the canvas
            _context.setTransform(1, 0, 0, 1, 0, 0);
            _context.clearRect(0, 0, width, height);
            _context.putImageData(_backgroundImage, 0, 0);

            // Restore the transform
            _context.restore();
        }

        function init() {
            var i;
            var margin = 20 * consts.LANES_PER_ROAD;
            _canvas = /** @type {HTMLCanvasElement} */ document.getElementById('canvas');
            _context = _canvas.getContext('2d');
            _fpsElem = document.getElementById('fps');

            width = _canvas.width;
            height = _canvas.height;
            world = new World(_canvas.width, _canvas.height);
            //for (i = 0; i < consts.NUM_RANDOM_ROADS; i += 1) {
            world.roads.push(Road.random([
                new Vector(margin, margin),
                new Vector(width - margin, margin),
                new Vector(width - margin, height - margin),
                new Vector(margin, height - margin),
                new Vector(margin, margin)
            ]));
            //}
            _context.setTransform(1, 0, 0, 1, 0, 0);
            _context.clearRect(0, 0, width, height);
            _backgroundImage = drawBackground(_canvas, _context, world);
            resetCanvas();
        }

        return {
            init: init,

            start: function () {
                var currentRunCount;
                var lastRedrawTime;
                var elapsedQueue = mathUtil.movingAverage(4);
                var context = _context;

                function drawFPS(fps) {
                    _fpsElem.innerHTML = '' + fps + ' fps';
//                context.fillStyle = 'black';
//                context.shadowBlur = 0;
//                context.fillText('' + fps + ' fps', 10, 10);
                }

                function updateDeltaT() {
                    var now = Date.now();
                    var elapsed = now - lastRedrawTime;
                    var averageElapsed = elapsedQueue.getResult();
                    var fps = Math.floor(1000.0 / averageElapsed);
                    lastRedrawTime = Date.now();
                    elapsedQueue.add(elapsed);
                    deltaT = elapsed / 1000.0;
                    /** @type {number} */
                    deltaTSquared = deltaT * deltaT;
                    drawFPS(fps);
                }

                function loop() {
                    updateDeltaT();
                    window.setTimeout(function () {
                        resetCanvas();
                        drawWorld(context, world);
                        simulateStep(world);
                        if (shouldStop[currentRunCount]) {
                            return;
                        }
                        loop();
                    }, (1000.0 / consts.TARGET_FPS));
                }

                stop();
                currentRunCount = runCount;
                loop();
            },

            stop: stop,

            clear: function () {
                resetCanvas();
                init();
            }
        };

    });
<commit_msg>Delete wrecked cars after a timeout<commit_after>/*global define, console */
define(['util/mathUtil', 'util/vector', 'util/car', 'util/road', 'util/consts'],
    function (mathUtil, Vector, Car, Road, consts) {
        'use strict';

        /** @type {number} */
        var deltaT = (1 / consts.TARGET_FPS);
        /** @type {number} */
        var deltaTSquared = deltaT * deltaT;

        /** @type {World} */
        var world;

        /** @type {number} */
        var width;
        /** @type {number} */
        var height;
        /** @type {number} */
        var runCount = 0;

        /** @type {Array.<boolean>} */
        var shouldStop = [];

        /** @type {HTMLCanvasElement} */
        var _canvas;
        /** @type {CanvasRenderingContext2D} */
        var _context;
        /** @type {ImageData} */
        var _backgroundImage;

        /** @type {HTMLElement} */
        var _fpsElem;

        /**
         * @param {number} width
         * @param {number} height
         * @constructor
         * @returns {{width:number, height:number, roads:Array.<Road>}}
         */
        function World(width, height) {
            this.width = width;
            this.height = height;
            this.roads = [];

        }

        /**
         * @param {CanvasRenderingContext2D} context
         * @param {World} world
         */
        function drawWorld(context, world) {
            var i;

            /**
             * @param {Road} road
             * @param {Car} car
             * @param {number} carIndex
             */
            function drawCar(road, car, carIndex) {
                var transform = road.roadToWorldPosition(car.position, car.lane);
                context.save();
                context.translate(transform.translate.x, transform.translate.y);
                context.rotate(Math.PI/2 - Math.atan2(transform.tangent.x, transform.tangent.y));
                car.draw(context);
                context.restore();
                //context.fillText(carIndex, drawPosition.x + 10, drawPosition.y + 10);
            }

            for (i = 0; i < world.roads.length; i += 1) {
                world.roads[i].forEachCar(drawCar);
            }
        }

        /**
         * @param {HTMLCanvasElement} canvas
         * @param {CanvasRenderingContext2D} context
         * @param {World} world
         * @returns {ImageData}
         */
        function drawBackground(canvas, context, world) {
            /** @type {Road} */
            var road;
            /** @type {number} */
            var i;
            for (i = 0; i < world.roads.length; i += 1) {
                road = world.roads[i];
                road.draw(context);
            }
            return context.getImageData(0, 0, canvas.width, canvas.height);
        }

        /**
         * @param {Road} road
         * @param {number} lane
         * @param {number} carIndex
         * @returns {{car:Car,index:number}}
         */
        function getNextCarInLane(road, lane, carIndex) {
            /** @type {number} */
            var i;
            /** @type {number} */
            var index;
            /** @type {Car} */
            var car;

            for (i = 0; i < road.cars.length - 1; i += 1) {
                index = (i + carIndex + 1) % road.cars.length;
                car = road.cars[index];
                if (car.lane === lane) {
                    return {
                        index: index,
                        car: car
                    };
                }
            }
            return null;
        }

        /**
         * @param {Road} road
         * @param {number} lane
         * @param {number} carIndex
         * @returns {{car:Car,index:number}}
         */
        function getPrevCarInLane(road, lane, carIndex) {
            /** @type {number} */
            var i;
            /** @type {number} */
            var index;
            /** @type {Car} */
            var car;

            for (i = 0; i < road.cars.length - 1; i += 1) {
                index = (carIndex - 1 - i + road.cars.length) % road.cars.length;
                car = road.cars[index];
                if (car.lane === lane) {
                    return {
                        index: index,
                        car: car
                    };
                }
            }
            return null;
        }

        /**
         * @param {Road} road
         * @param {number} laneNum
         * @param {number} carIndex
         * @returns {number}
         */
        function spaceAvailableInLane(road, laneNum, carIndex) {
            /** @type {Car} */
            var car = road.cars[carIndex];
            var minSwitchSpace = car.length * (2 + 1.0 * (Math.min(1, car.speed)));
            var nextCarInfo = getNextCarInLane(road, laneNum, carIndex);
            var prevCarInfo = getPrevCarInLane(road, laneNum, carIndex);
            /** @type {number} */
            var forwardSpace;
            /** @type {number} */
            var backwardSpace;

            if (!nextCarInfo || !prevCarInfo) {
                if (!nextCarInfo && !prevCarInfo) {
                    return road.length - car.length;
                }
                return 0;
            }
            forwardSpace = (nextCarInfo.index < carIndex ? -1 : 1) * (nextCarInfo.car.position - car.position) - car.length;
            backwardSpace = (carIndex < prevCarInfo.index ? -1 : 1) * (car.position - prevCarInfo.car.position) - prevCarInfo.car.length;

            if ((forwardSpace < minSwitchSpace) || (backwardSpace < minSwitchSpace)) {
                return 0;
            }
            return forwardSpace;
        }

        /**
         * @param {Road} road
         * @param {Car} car
         * @param {number} carIndex
         * @returns {number}
         */
        function decideAcceleration(road, car, carIndex) {
            var distanceToNextCarBackside;
            var closingSpeed;
            var nextCarRelativePosition;
            var impactTime;
            var keepingTime;
            var nextAccel;
            var nextCar;
            if (car.wrecked) {
                return 0;
            }
            nextCar = getNextCarInLane(road, car.lane, carIndex).car;
            if (!nextCar) {
                return car.maxAcceleration;
            }
            nextAccel = car.maxAcceleration;
            nextCarRelativePosition = nextCar.position + (nextCar.position < car.position ? road.length : 0);
            closingSpeed = car.speed - nextCar.speed;
            distanceToNextCarBackside = nextCarRelativePosition - car.position - car.length;
            if (distanceToNextCarBackside < 0) {
                // Wreck!
                console.log('Collision!', car.color, nextCar.color);
                car.wreck();
                car.wreckTTL = 10;
                nextCar.wreck();
                nextCar.wreckTTL = 10;
                //debugger;
                return 0;
            }
            if (closingSpeed > 0) {
                impactTime = distanceToNextCarBackside / closingSpeed;
                if (impactTime < consts.MIN_IMPACT_TIME) {
                    nextAccel = -closingSpeed / (impactTime / 2);
                }
            }
            //else {
            // never going to impact, closing speed is negative (actually gaining distance) or very, very small.
            //}

            keepingTime = distanceToNextCarBackside / car.speed;
            if (keepingTime < car.minKeepingTime) {
                // too close for comfort
                nextAccel = Math.min(nextAccel, -car.speed / keepingTime);
            }

            if (distanceToNextCarBackside < consts.MIN_KEEPING_DISTANCE) {
                nextAccel = Math.min(nextAccel, distanceToNextCarBackside - consts.MIN_KEEPING_DISTANCE);
            }

            //console.log('Car:', car.color, 'Distance:', distanceToNextCarBackside, 'Closing speed:', closingSpeed, 'Impact time:', impactTime, 'Accel: ', car.accel);
            if (nextAccel > 0) {
                nextAccel = Math.min(nextAccel, car.maxAcceleration);
            }
            return nextAccel;
        }

        /**
         * @param {Road} road
         * @param {Car} car
         * @param {number} carIndex
         * @returns {number}
         */
        function decideNextLane(road, car, carIndex) {
            var spaceInCurrentLane;
            var spaceInOtherLane;
            var nextLane = car.lane;
            if (car.wrecked) {
                return car.lane;
            }
            spaceInCurrentLane = spaceAvailableInLane(road, car.lane, carIndex);
            if (spaceInCurrentLane > car.length * 10) {
                return car.lane; // plenty of space in current lane.
            }
            if (car.lane < road.numLanes - 1) {
                spaceInOtherLane = spaceAvailableInLane(road, car.lane + 1, carIndex);
                if (spaceInOtherLane > spaceInCurrentLane) {
//                    console.log(car.color, 'Switching lanes. Current lane: ', car.lane,
//                        'Position: ', car.position,
//                        'Space in this lane:', spaceInCurrentLane, 'in other lane:', spaceInOtherLane);
                    nextLane = car.lane + 1;
                }
            }
            if (nextLane === car.lane && (car.lane > 0)) {
                spaceInOtherLane = spaceAvailableInLane(road, car.lane - 1, carIndex);
                if (spaceInOtherLane > spaceInCurrentLane) {
//                    console.log(car.color, 'Switching lanes. Current lane: ', car.lane,
//                        'Position: ', car.position,
//                        ' Space in this lane:', spaceInCurrentLane, 'in other lane:', spaceInOtherLane);
                    nextLane = car.lane - 1;
                }
            }
            return nextLane;
        }

        /**
         * @param {Road} road
         * @param {Car} car
         */
        function simulateNewtonMechanics(road, car) {
            car.speed = Math.min(car.maxSpeed, Math.max(0, car.speed + car.accel * deltaT));
            car.position += car.speed * deltaT + 0.5 * car.accel * deltaTSquared;

            while (car.position > road.length) {
                car.position -= road.length;
            }
            while (car.position < 0) {
                car.position += road.length;
            }
        }

        /**
         * @param {Road} road
         * @param {Car} car
         * @param {number} carIndex
         * @returns {Function|null}
         */
        function simulateCar(road, car, carIndex) {
            var nextLane = decideNextLane(road, car, carIndex);
            var nextAccel = decideAcceleration(road, car, carIndex);
            if (car.wrecked) {
                car.wreckTTL -= deltaT;
                if (0 >= car.wreckTTL) {
                    road.cars.splice(carIndex, 1);
                }
                return null;
            }

            car.accel = nextAccel;
            simulateNewtonMechanics(road, car);

            return function () {
                car.lane = nextLane;
            };
        }

        function simulateStep(world) {
            var i, j;
            var carUpdater;
            var carUpdaters;
            /** @type {Road} */
            var road;
            for (i = 0; i < world.roads.length; i += 1) {
                road = world.roads[i];
                carUpdaters = road.forEachCar(simulateCar);
                for (j = 0; j < carUpdaters.length; j += 1) {
                    carUpdater = carUpdaters[j];
                    if (carUpdater) {
                        carUpdater();
                    }
                }
                road.sortCars();
            }
        }

        function stop() {
            shouldStop[runCount] = true;
            runCount += 1;
        }

        function resetCanvas() {
            // Store the current transformation matrix
            _context.save();

            // Use the identity matrix while clearing the canvas
            _context.setTransform(1, 0, 0, 1, 0, 0);
            _context.clearRect(0, 0, width, height);
            _context.putImageData(_backgroundImage, 0, 0);

            // Restore the transform
            _context.restore();
        }

        function init() {
            var i;
            var margin = 20 * consts.LANES_PER_ROAD;
            _canvas = /** @type {HTMLCanvasElement} */ document.getElementById('canvas');
            _context = _canvas.getContext('2d');
            _fpsElem = document.getElementById('fps');

            width = _canvas.width;
            height = _canvas.height;
            world = new World(_canvas.width, _canvas.height);
            //for (i = 0; i < consts.NUM_RANDOM_ROADS; i += 1) {
            world.roads.push(Road.random([
                new Vector(margin, margin),
                new Vector(width - margin, margin),
                new Vector(width - margin, height - margin),
                new Vector(margin, height - margin),
                new Vector(margin, margin)
            ]));
            //}
            _context.setTransform(1, 0, 0, 1, 0, 0);
            _context.clearRect(0, 0, width, height);
            _backgroundImage = drawBackground(_canvas, _context, world);
            resetCanvas();
        }

        return {
            init: init,

            start: function () {
                var currentRunCount;
                var lastRedrawTime;
                var elapsedQueue = mathUtil.movingAverage(4);
                var context = _context;

                function drawFPS(fps) {
                    _fpsElem.innerHTML = '' + fps + ' fps';
//                context.fillStyle = 'black';
//                context.shadowBlur = 0;
//                context.fillText('' + fps + ' fps', 10, 10);
                }

                function updateDeltaT() {
                    var now = Date.now();
                    var elapsed = now - lastRedrawTime;
                    var averageElapsed = elapsedQueue.getResult();
                    var fps = Math.floor(1000.0 / averageElapsed);
                    lastRedrawTime = Date.now();
                    elapsedQueue.add(elapsed);
                    deltaT = elapsed / 1000.0;
                    /** @type {number} */
                    deltaTSquared = deltaT * deltaT;
                    drawFPS(fps);
                }

                function loop() {
                    updateDeltaT();
                    window.setTimeout(function () {
                        resetCanvas();
                        drawWorld(context, world);
                        simulateStep(world);
                        if (shouldStop[currentRunCount]) {
                            return;
                        }
                        loop();
                    }, (1000.0 / consts.TARGET_FPS));
                }

                stop();
                currentRunCount = runCount;
                loop();
            },

            stop: stop,

            clear: function () {
                resetCanvas();
                init();
            }
        };

    });
<|endoftext|>