<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class therock extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'therock',
            'name': 'TheRockTrading',
            'countries': [ 'MT' ],
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'fetchMyTrades': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg',
                'api': 'https://api.therocktrading.com',
                'www': 'https://therocktrading.com',
                'doc': [
                    'https://api.therocktrading.com/doc/v1/index.html',
                    'https://api.therocktrading.com/doc/',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'funds',
                        'funds/{id}/orderbook',
                        'funds/{id}/ticker',
                        'funds/{id}/trades',
                        'funds/tickers',
                    ],
                },
                'private': {
                    'get': [
                        'balances',
                        'balances/{id}',
                        'discounts',
                        'discounts/{id}',
                        'funds',
                        'funds/{id}',
                        'funds/{id}/trades',
                        'funds/{fund_id}/orders',
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/position_balances',
                        'funds/{fund_id}/positions',
                        'funds/{fund_id}/positions/{id}',
                        'transactions',
                        'transactions/{id}',
                        'withdraw_limits/{id}',
                        'withdraw_limits',
                    ],
                    'post': [
                        'atms/withdraw',
                        'funds/{fund_id}/orders',
                    ],
                    'delete': [
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/orders/remove_all',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'tierBased': false,
                    'percentage': false,
                    'withdraw': {
                        'BTC': 0.0005,
                        'BCH': 0.0005,
                        'PPC': 0.02,
                        'ETH': 0.001,
                        'ZEC': 0.001,
                        'LTC': 0.002,
                        'EUR': 2.5,  // worst-case scenario: https://therocktrading.com/en/pages/fees
                    },
                    'deposit': {
                        'BTC': 0,
                        'BCH': 0,
                        'PPC': 0,
                        'ETH': 0,
                        'ZEC': 0,
                        'LTC': 0,
                        'EUR': 0,
                    },
                },
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetFunds ();
        //
        //     { funds: [ {                      id:   "BTCEUR",
        //                              description:   "Trade Bitcoin with Euro",
        //                                     type:   "currency",
        //                            base_currency:   "EUR",
        //                           trade_currency:   "BTC",
        //                                  buy_fee:    0.2,
        //                                 sell_fee:    0.2,
        //                      minimum_price_offer:    0.01,
        //                   minimum_quantity_offer:    0.0005,
        //                   base_currency_decimals:    2,
        //                  trade_currency_decimals:    4,
        //                                leverages: []                           },
        //                {                      id:   "LTCEUR",
        //                              description:   "Trade Litecoin with Euro",
        //                                     type:   "currency",
        //                            base_currency:   "EUR",
        //                           trade_currency:   "LTC",
        //                                  buy_fee:    0.2,
        //                                 sell_fee:    0.2,
        //                      minimum_price_offer:    0.01,
        //                   minimum_quantity_offer:    0.01,
        //                   base_currency_decimals:    2,
        //                  trade_currency_decimals:    2,
        //                                leverages: []                            } ] }
        //
        let markets = this.safeValue (response, 'funds');
        let result = [];
        if (markets === undefined) {
            throw new ExchangeError (this.id + ' fetchMarkets got an unexpected response');
        } else {
            for (let i = 0; i < markets.length; i++) {
                let market = markets[i];
                let id = this.safeString (market, 'id');
                let baseId = this.safeString (market, 'trade_currency');
                let quoteId = this.safeString (market, 'base_currency');
                let base = this.commonCurrencyCode (baseId);
                let quote = this.commonCurrencyCode (quoteId);
                let symbol = base + '/' + quote;
                let buy_fee = this.safeFloat (market, 'buy_fee');
                let sell_fee = this.safeFloat (market, 'sell_fee');
                let taker = Math.max (buy_fee, sell_fee);
                taker = taker / 100;
                let maker = taker;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                    'active': true,
                    'maker': maker,
                    'taker': taker,
                    'precision': {
                        'amount': this.safeInteger (market, 'trade_currency_decimals'),
                        'price': this.safeInteger (market, 'base_currency_decimals'),
                    },
                    'limits': {
                        'amount': {
                            'min': this.safeFloat (market, 'minimum_quantity_offer'),
                            'max': undefined,
                        },
                        'price': {
                            'min': this.safeFloat (market, 'minimum_price_offer'),
                            'max': undefined,
                        },
                        'cost': {
                            'min': undefined,
                            'max': undefined,
                        },
                    },
                });
            }
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetBalances ();
        let balances = response['balances'];
        let result = { 'info': response };
        for (let b = 0; b < balances.length; b++) {
            let balance = balances[b];
            let currency = balance['currency'];
            let free = balance['trading_balance'];
            let total = balance['balance'];
            let used = total - free;
            let account = {
                'free': free,
                'used': used,
                'total': total,
            };
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetFundsIdOrderbook (this.extend ({
            'id': this.marketId (symbol),
        }, params));
        let timestamp = this.parse8601 (orderbook['date']);
        return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount');
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.parse8601 (ticker['date']);
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = this.safeFloat (ticker, 'last');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': this.safeFloat (ticker, 'close'), // previous day close, if any
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'volume_traded'),
            'quoteVolume': this.safeFloat (ticker, 'volume'),
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetFundsTickers (params);
        let tickers = this.indexBy (response['tickers'], 'fund_id');
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetFundsIdTicker (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market = undefined) {
        if (!market)
            market = this.markets_by_id[trade['fund_id']];
        let timestamp = this.parse8601 (trade['date']);
        return {
            'info': trade,
            'id': trade['id'].toString (),
            'order': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': trade['side'],
            'price': trade['price'],
            'amount': trade['amount'],
        };
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'id': market['id'],
        };
        const response = await this.privateGetFundsIdTrades (this.extend (request, params));
        //
        //     { trades: [ {           id:    237338,
        //                        fund_id:   "BTCEUR",
        //                         amount:    0.348,
        //                          price:    348,
        //                           side:   "sell",
        //                           dark:    false,
        //                       order_id:    14920648,
        //                           date:   "2015-06-03T00:49:49.000Z",
        //                   transactions: [ {       id:  2770768,
        //                                         date: "2015-06-03T00:49:49.000Z",
        //                                         type: "sold_currency_to_fund",
        //                                        price:  121.1,
        //                                     currency: "EUR"                       },
        //                                   {       id:  2770769,
        //                                         date: "2015-06-03T00:49:49.000Z",
        //                                         type: "released_currency_to_fund",
        //                                        price:  0.348,
        //                                     currency: "BTC"                        },
        //                                   {       id:  2770772,
        //                                         date: "2015-06-03T00:49:49.000Z",
        //                                         type: "paid_commission",
        //                                        price:  0.06,
        //                                     currency: "EUR",
        //                                     trade_id:  440492                     }   ] } ],
        //         meta: { total_count:    31,
        //                       first: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" },
        //                    previous:    null,
        //                     current: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" },
        //                        next: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" },
        //                        last: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" }  } }
        //
        return this.parseTrades (response['trades'], market, since, limit);
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetFundsIdTrades (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTrades (response['trades'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        if (type === 'market')
            price = 0;
        let response = await this.privatePostFundsFundIdOrders (this.extend ({
            'fund_id': this.marketId (symbol),
            'side': side,
            'amount': amount,
            'price': price,
        }, params));
        return {
            'info': response,
            'id': response['id'].toString (),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privateDeleteFundsFundIdOrdersId (this.extend ({
            'id': id,
            'fund_id': this.marketId (symbol),
        }, params));
    }

    parseOrderStatus (status) {
        const statuses = {
            'active': 'open',
            'executed': 'closed',
            'deleted': 'canceled',
            // don't know what this status means
            // 'conditional': '?',
        };
        return this.safeString (statuses, status, status);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let auth = nonce + url;
            headers = {
                'X-TRT-KEY': this.apiKey,
                'X-TRT-NONCE': nonce,
                'X-TRT-SIGN': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'),
            };
            if (Object.keys (query).length) {
                body = this.json (query);
                headers['Content-Type'] = 'application/json';
            }
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('errors' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<commit_msg>therock fetchTrades comments<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class therock extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'therock',
            'name': 'TheRockTrading',
            'countries': [ 'MT' ],
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'fetchMyTrades': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg',
                'api': 'https://api.therocktrading.com',
                'www': 'https://therocktrading.com',
                'doc': [
                    'https://api.therocktrading.com/doc/v1/index.html',
                    'https://api.therocktrading.com/doc/',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'funds',
                        'funds/{id}/orderbook',
                        'funds/{id}/ticker',
                        'funds/{id}/trades',
                        'funds/tickers',
                    ],
                },
                'private': {
                    'get': [
                        'balances',
                        'balances/{id}',
                        'discounts',
                        'discounts/{id}',
                        'funds',
                        'funds/{id}',
                        'funds/{id}/trades',
                        'funds/{fund_id}/orders',
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/position_balances',
                        'funds/{fund_id}/positions',
                        'funds/{fund_id}/positions/{id}',
                        'transactions',
                        'transactions/{id}',
                        'withdraw_limits/{id}',
                        'withdraw_limits',
                    ],
                    'post': [
                        'atms/withdraw',
                        'funds/{fund_id}/orders',
                    ],
                    'delete': [
                        'funds/{fund_id}/orders/{id}',
                        'funds/{fund_id}/orders/remove_all',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'tierBased': false,
                    'percentage': false,
                    'withdraw': {
                        'BTC': 0.0005,
                        'BCH': 0.0005,
                        'PPC': 0.02,
                        'ETH': 0.001,
                        'ZEC': 0.001,
                        'LTC': 0.002,
                        'EUR': 2.5,  // worst-case scenario: https://therocktrading.com/en/pages/fees
                    },
                    'deposit': {
                        'BTC': 0,
                        'BCH': 0,
                        'PPC': 0,
                        'ETH': 0,
                        'ZEC': 0,
                        'LTC': 0,
                        'EUR': 0,
                    },
                },
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetFunds ();
        //
        //     { funds: [ {                      id:   "BTCEUR",
        //                              description:   "Trade Bitcoin with Euro",
        //                                     type:   "currency",
        //                            base_currency:   "EUR",
        //                           trade_currency:   "BTC",
        //                                  buy_fee:    0.2,
        //                                 sell_fee:    0.2,
        //                      minimum_price_offer:    0.01,
        //                   minimum_quantity_offer:    0.0005,
        //                   base_currency_decimals:    2,
        //                  trade_currency_decimals:    4,
        //                                leverages: []                           },
        //                {                      id:   "LTCEUR",
        //                              description:   "Trade Litecoin with Euro",
        //                                     type:   "currency",
        //                            base_currency:   "EUR",
        //                           trade_currency:   "LTC",
        //                                  buy_fee:    0.2,
        //                                 sell_fee:    0.2,
        //                      minimum_price_offer:    0.01,
        //                   minimum_quantity_offer:    0.01,
        //                   base_currency_decimals:    2,
        //                  trade_currency_decimals:    2,
        //                                leverages: []                            } ] }
        //
        let markets = this.safeValue (response, 'funds');
        let result = [];
        if (markets === undefined) {
            throw new ExchangeError (this.id + ' fetchMarkets got an unexpected response');
        } else {
            for (let i = 0; i < markets.length; i++) {
                let market = markets[i];
                let id = this.safeString (market, 'id');
                let baseId = this.safeString (market, 'trade_currency');
                let quoteId = this.safeString (market, 'base_currency');
                let base = this.commonCurrencyCode (baseId);
                let quote = this.commonCurrencyCode (quoteId);
                let symbol = base + '/' + quote;
                let buy_fee = this.safeFloat (market, 'buy_fee');
                let sell_fee = this.safeFloat (market, 'sell_fee');
                let taker = Math.max (buy_fee, sell_fee);
                taker = taker / 100;
                let maker = taker;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                    'active': true,
                    'maker': maker,
                    'taker': taker,
                    'precision': {
                        'amount': this.safeInteger (market, 'trade_currency_decimals'),
                        'price': this.safeInteger (market, 'base_currency_decimals'),
                    },
                    'limits': {
                        'amount': {
                            'min': this.safeFloat (market, 'minimum_quantity_offer'),
                            'max': undefined,
                        },
                        'price': {
                            'min': this.safeFloat (market, 'minimum_price_offer'),
                            'max': undefined,
                        },
                        'cost': {
                            'min': undefined,
                            'max': undefined,
                        },
                    },
                });
            }
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetBalances ();
        let balances = response['balances'];
        let result = { 'info': response };
        for (let b = 0; b < balances.length; b++) {
            let balance = balances[b];
            let currency = balance['currency'];
            let free = balance['trading_balance'];
            let total = balance['balance'];
            let used = total - free;
            let account = {
                'free': free,
                'used': used,
                'total': total,
            };
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetFundsIdOrderbook (this.extend ({
            'id': this.marketId (symbol),
        }, params));
        let timestamp = this.parse8601 (orderbook['date']);
        return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount');
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.parse8601 (ticker['date']);
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = this.safeFloat (ticker, 'last');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': this.safeFloat (ticker, 'close'), // previous day close, if any
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'volume_traded'),
            'quoteVolume': this.safeFloat (ticker, 'volume'),
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetFundsTickers (params);
        let tickers = this.indexBy (response['tickers'], 'fund_id');
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetFundsIdTicker (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market = undefined) {
        if (!market)
            market = this.markets_by_id[trade['fund_id']];
        let timestamp = this.parse8601 (trade['date']);
        return {
            'info': trade,
            'id': trade['id'].toString (),
            'order': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': trade['side'],
            'price': trade['price'],
            'amount': trade['amount'],
        };
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'id': market['id'],
        };
        const response = await this.privateGetFundsIdTrades (this.extend (request, params));
        //
        //     { trades: [ {           id:    237338,
        //                        fund_id:   "BTCEUR",
        //                         amount:    0.348,
        //                          price:    348,
        //                           side:   "sell",
        //                           dark:    false,
        //                       order_id:    14920648,
        //                           date:   "2015-06-03T00:49:49.000Z",
        //                   transactions: [ {       id:  2770768,
        //                                         date: "2015-06-03T00:49:49.000Z",
        //                                         type: "sold_currency_to_fund",
        //                                        price:  121.1,
        //                                     currency: "EUR"                       },
        //                                   {       id:  2770769,
        //                                         date: "2015-06-03T00:49:49.000Z",
        //                                         type: "released_currency_to_fund",
        //                                        price:  0.348,
        //                                     currency: "BTC"                        },
        //                                   {       id:  2770772,
        //                                         date: "2015-06-03T00:49:49.000Z",
        //                                         type: "paid_commission",
        //                                        price:  0.06,
        //                                     currency: "EUR",
        //                                     trade_id:  440492                     }   ] } ],
        //         meta: { total_count:    31,
        //                       first: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" },
        //                    previous:    null,
        //                     current: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" },
        //                        next: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" },
        //                        last: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" }  } }
        //
        return this.parseTrades (response['trades'], market, since, limit);
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'id': market['id'],
        };
        const response = await this.publicGetFundsIdTrades (this.extend (request, params));
        //
        //     { trades: [ {      id:  4493548,
        //                   fund_id: "ETHBTC",
        //                    amount:  0.203,
        //                     price:  0.02783576,
        //                      side: "buy",
        //                      dark:  false,
        //                      date: "2018-11-30T08:19:18.236Z" },
        //                 {      id:  4492926,
        //                   fund_id: "ETHBTC",
        //                    amount:  0.04,
        //                     price:  0.02767034,
        //                      side: "buy",
        //                      dark:  false,
        //                      date: "2018-11-30T07:03:03.897Z" }  ],
        //         meta: { total_count:    null,
        //                       first: { page:  1,
        //                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" },
        //                    previous:    null,
        //                     current: { page:  1,
        //                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" },
        //                        next: { page:  2,
        //                                href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2" },
        //                        last:    null                                                                   } }
        //
        return this.parseTrades (response['trades'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        if (type === 'market')
            price = 0;
        let response = await this.privatePostFundsFundIdOrders (this.extend ({
            'fund_id': this.marketId (symbol),
            'side': side,
            'amount': amount,
            'price': price,
        }, params));
        return {
            'info': response,
            'id': response['id'].toString (),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privateDeleteFundsFundIdOrdersId (this.extend ({
            'id': id,
            'fund_id': this.marketId (symbol),
        }, params));
    }

    parseOrderStatus (status) {
        const statuses = {
            'active': 'open',
            'executed': 'closed',
            'deleted': 'canceled',
            // don't know what this status means
            // 'conditional': '?',
        };
        return this.safeString (statuses, status, status);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let auth = nonce + url;
            headers = {
                'X-TRT-KEY': this.apiKey,
                'X-TRT-NONCE': nonce,
                'X-TRT-SIGN': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'),
            };
            if (Object.keys (query).length) {
                body = this.json (query);
                headers['Content-Type'] = 'application/json';
            }
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('errors' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<|endoftext|>


<commit_before>'use strict';

var curGroup = null;

function Dome(parent, type, className, name) {
    this.type = type;
    if (parent instanceof Dome) parent = parent.elt;
    var elt = this.elt = parent.appendChild(document.createElement(type));
    if (name && name[0] === '#') {
        // name starts with # so remove it from className and add this to current group
        className = className.substr(1);
        curGroup.add(name.substr(1), this);
    }
    if (className) elt.className = className;
}
module.exports = Dome;

Dome.deleteChild = function (parent, dome) {
    if (parent instanceof Dome) parent = parent.elt;
    parent.removeChild(dome.elt);
};

Dome.prototype.clear = function () { this.elt.innerHTML = ''; };
Dome.prototype.setText = function (text) { this.elt.textContent = text; return this; };
Dome.prototype.setHtml = function (html) { this.elt.innerHTML = html; return this; };
Dome.prototype.setAttribute = function (name, val) { this.elt.setAttribute(name, val); return this; };
Dome.prototype.setEnabled = function (enable) { this.elt.disabled = !enable; return this; };
Dome.prototype.setVisible = function (show) { this.elt.hidden = !show; return this; };

Dome.prototype.text = function () { return this.elt.textContent; };
Dome.prototype.html = function () { return this.elt.innerHTML; };
Dome.prototype.value = function () { return this.elt.value; };

Dome.prototype.toggleClass = function (className, enable) {
    var elt = this.elt;
    var classes = elt.className.split(' ');
    var ndx = classes.indexOf(className);
    if (enable) {
        if (ndx >= 0) return;
        elt.className += ' ' + className;
    } else {
        if (ndx < 0) return;
        classes.splice(ndx, 1);
        elt.className = classes.join(' ');
    }
};

Dome.newDiv = function (parent, className) {
    return new Dome(parent, 'div', className);
};
// object like helper for newDiv
Dome.prototype.newDiv = function (className) { return new Dome(this, 'div', className); };

Dome.newButton = function (parent, name, label, action) {
    var button = new Dome(parent, 'button', name + 'Button', name);
    var btn = button.elt;
    btn.innerText = label;
    btn.addEventListener('click', action);
    return button;
};

/** A label is a span = helps to write text on the left of an element */
Dome.newLabel = function (parent, name, label) {
    return new Dome(parent, 'span', name, name).setText(label);
};

Dome.newInput = function (parent, name, label, init) {
    var labelName = name + 'Label';
    Dome.newLabel(parent, labelName + ' inputLbl', label, labelName);
    var input = new Dome(parent, 'input', name + 'Input inputBox', name);
    if (init !== undefined) input.elt.value = init;
    return input;
};

/** e.g. Dome.newRadio(parent, 'stoneColor', ['white', 'black'], null, 'white') */
Dome.newRadio = function (parent, name, labels, values, init) {
    if (!values) values = labels;
    var opts = [];
    for (var i = 0; i < labels.length; i++) {
        var input = opts[i] = new Dome(parent, 'input', name + 'RadioBtn radioBtn', name);
        var inp = input.elt;
        inp.type = 'radio';
        inp.name = name;
        inp.value = values[i];
        inp.id = name + 'Radio' + values[i];
        if (values[i] === init) inp.checked = true;
        var label = new Dome(parent, 'label', name + 'RadioLabel radioLbl', name).elt;
        label.textContent = labels[i];
        label.setAttribute('for', inp.id);
    }
    return opts;
};

/** @param {array} opts - the array of options returned when you created the radio buttons with newRadio */
Dome.getRadioValue = function (opts) {
    for (var i = 0; i < opts.length; i++) {
        if (opts[i].elt.checked) return opts[i].elt.value;
    }
};

//---Group helpers

function DomeGroup() {
    this.ctrl = {};
}

Dome.newGroup = function () {
    curGroup = new DomeGroup();
    return curGroup;
};

DomeGroup.prototype.add = function (name, dome) { this.ctrl[name] = dome; };
DomeGroup.prototype.get = function (name) { return this.ctrl[name]; };

DomeGroup.prototype.setEnabled = function (names, enabled, except) {
    if (names === 'ALL') names = Object.keys(this.ctrl);
    for (var i = 0; i < names.length; i++) {
        if (except && except.indexOf(names[i]) !== -1) continue;
        this.ctrl[names[i]].setEnabled(enabled);
    }
};

DomeGroup.prototype.setVisible = function (names, show, except) {
    if (names === 'ALL') names = Object.keys(this.ctrl);
    for (var i = 0; i < names.length; i++) {
        if (except && except.indexOf(names[i]) !== -1) continue;
        this.ctrl[names[i]].setVisible(show);
    }
};
<commit_msg>Dome handles dropdowns + some refact<commit_after>'use strict';

var curGroup = null;

function Dome(parent, type, className, name) {
    this.type = type;
    if (parent instanceof Dome) parent = parent.elt;
    var elt = this.elt = parent.appendChild(document.createElement(type));
    if (name && name[0] === '#') {
        // name starts with # so remove it from className and add this to current group
        className = className.substr(1);
        curGroup.add(name.substr(1), this);
    }
    if (className) elt.className = className;
}
module.exports = Dome;

// Setters

Dome.prototype.clear = function () { this.elt.innerHTML = ''; };
Dome.prototype.setText = function (text) { this.elt.textContent = text; return this; };
Dome.prototype.setHtml = function (html) { this.elt.innerHTML = html; return this; };
Dome.prototype.setAttribute = function (name, val) { this.elt.setAttribute(name, val); return this; };
Dome.prototype.setEnabled = function (enable) { this.elt.disabled = !enable; return this; };
Dome.prototype.setVisible = function (show) { this.elt.hidden = !show; return this; };

// Getters

Dome.prototype.text = function () { return this.elt.textContent; };
Dome.prototype.html = function () { return this.elt.innerHTML; };
Dome.prototype.value = function () { return this.elt.value; };
Dome.prototype.getDomElt = function () { return this.elt; };

Dome.prototype.toggleClass = function (className, enable) {
    var elt = this.elt;
    var classes = elt.className.split(' ');
    var ndx = classes.indexOf(className);
    if (enable) {
        if (ndx >= 0) return;
        elt.className += ' ' + className;
    } else {
        if (ndx < 0) return;
        classes.splice(ndx, 1);
        elt.className = classes.join(' ');
    }
};

Dome.newDiv = function (parent, className) {
    return new Dome(parent, 'div', className);
};
Dome.prototype.newDiv = function (className) { return new Dome(this, 'div', className); };

Dome.removeChild = function (parent, dome) {
    if (parent instanceof Dome) parent = parent.elt;
    parent.removeChild(dome.elt);
};
Dome.prototype.removeChild = function (child) { this.elt.removeChild(child.elt); };

Dome.newButton = function (parent, name, label, action) {
    var button = new Dome(parent, 'button', name + 'Button', name);
    var btn = button.elt;
    btn.innerText = label;
    btn.addEventListener('click', action);
    return button;
};

/** A label is a span = helps to write text on the left of an element */
Dome.newLabel = function (parent, name, label) {
    return new Dome(parent, 'span', name, name).setText(label);
};

Dome.newInput = function (parent, name, label, init) {
    var labelName = name + 'Label';
    Dome.newLabel(parent, labelName + ' inputLbl', label, labelName);
    var input = new Dome(parent, 'input', name + 'Input inputBox', name);
    if (init !== undefined) input.elt.value = init;
    return input;
};

/** var myOptions = Dome.newRadio(parent, 'stoneColor', ['white', 'black'], null, 'white');
 *  ...
 *  var result = Dome.getRadioValue(myOptions);
 */
Dome.newRadio = function (parent, name, labels, values, init) {
    if (!values) values = labels;
    var opts = [];
    for (var i = 0; i < labels.length; i++) {
        var input = opts[i] = new Dome(parent, 'input', name + 'RadioBtn radioBtn', name);
        var inp = input.elt;
        inp.type = 'radio';
        inp.name = name;
        inp.value = values[i];
        inp.id = name + 'Radio' + values[i];
        if (values[i] === init) inp.checked = true;
        var label = new Dome(parent, 'label', name + 'RadioLabel radioLbl', name).elt;
        label.textContent = labels[i];
        label.setAttribute('for', inp.id);
    }
    return opts;
};

/** @param {array} opts - the array of options returned when you created the radio buttons with newRadio */
Dome.getRadioValue = function (opts) {
    for (var i = 0; i < opts.length; i++) {
        if (opts[i].elt.checked) return opts[i].elt.value;
    }
};

/** var mySelect = Dome.newDropdown(parent, 'stoneColor', ['white', 'black'], null, 'white')
 *  ...
 *  mySelect.value()
 */
Dome.newDropdown = function (parent, name, labels, values, init) {
    if (!values) values = labels;
    var select = new Dome(parent, 'select', name + 'Dropdwn dropdwn');
    var cur = 0;
    for (var i = 0; i < labels.length; i++) {
        var opt = new Dome(select, 'option').elt;
        opt.value = values[i];
        opt.textContent = labels[i];
        if (values[i] === init) cur = i;
    }
    select.elt.selectedIndex = cur;
    return select;
};

//---Group helpers

function DomeGroup() {
    this.ctrl = {};
}

Dome.newGroup = function () {
    curGroup = new DomeGroup();
    return curGroup;
};

DomeGroup.prototype.add = function (name, dome) { this.ctrl[name] = dome; };
DomeGroup.prototype.get = function (name) { return this.ctrl[name]; };

DomeGroup.prototype.setEnabled = function (names, enabled, except) {
    if (names === 'ALL') names = Object.keys(this.ctrl);
    for (var i = 0; i < names.length; i++) {
        if (except && except.indexOf(names[i]) !== -1) continue;
        this.ctrl[names[i]].setEnabled(enabled);
    }
};

DomeGroup.prototype.setVisible = function (names, show, except) {
    if (names === 'ALL') names = Object.keys(this.ctrl);
    for (var i = 0; i < names.length; i++) {
        if (except && except.indexOf(names[i]) !== -1) continue;
        this.ctrl[names[i]].setVisible(show);
    }
};
<|endoftext|>


<commit_before>// --- Util ---
function makeEditButton(onclick) {
  return $('
Edit
').on('click', onclick);
}
function makeURL(path) {
  return '/' + path;
  return 'https://bhasa.herokuapp.com/' + path;
}
function runJSON(method, path, query, callback) {
  console.log(query);

  var settings = {
    'method': method,
    'url': makeURL(path),
    'data': query,
    // 'dataType': 'json',
    'complete': callback,
  };

  if (method !== 'GET' && method !== 'HEAD')
    settings.contentType = 'application/json';

  $.ajax(settings);
}
function divWithClass(cl) {
  var div = $("
");
  if (cl !== null)
    div.addClass(cl);
  return div;
}
function tagWithClass(name, cl) {
  var el = $("<" + name + ">");
  if (cl !== null)
    el.addClass(cl);
  return el;
}
function codeMirrorGoEnd(cm) {
  cm.execCommand("goDocEnd"); // try once
  setTimeout(function() { cm.execCommand("goDocEnd"); }, 1); // try again
}
function makeCodeMirror(el, mode, txt) {
  console.log(el.get(0));
  var cm = CodeMirror(el[0], {
    viewportMargin: Infinity,
    mode: mode,
    tabSize: 2,
    // lineNumbers: true,
    autofocus: true,
    lineWrapping: true,
    theme: 'cmtn',
    value: txt,
  });
  codeMirrorGoEnd(cm);

  return cm;
}

// --- Rendering ---
function _renderSearchResult(r) {
  var result = divWithClass("pl_item");
  result.addClass('noselect');
  result.text(r.title);
  return result;
}
function _renderSearchForm() {
  var search = $('<input class="searchfield" type="search" placeholder="search e.g. map()">');

  var search_results = $('
');
  search_results.addClass("pl_list")
  search_results.append(_renderSearchResult({ title: "Hello" }));
  search_results.append(_renderSearchResult({ title: "Hello" }));

  var doSearch = _.debounce(function() {
    var query = search.val();
    runJSON('GET', 'api/search', { 'q': query }, function(data) {
      console.log(data);
      data = data.responseJSON;
      console.log(data);

      var rs = data.results;
      var els = [];
      rs.forEach(function(r) {
        els.push(_renderSearchResult(r));
      });

      search_results.empty();
      search_results.append(els);

    }, 'json');
  }, 500);

  search.on('input', function() {
    doSearch();
  });

  return [search, search_results];
}
function renderHome() {
  var content = divWithClass("home");

  var tagline = tagWithClass("p", "tagline");
  tagline.html("bhasa is the programming language that anyone can edit.");
  content.append(tagline);

  content.append(_renderSearchForm());
  return content;
}
function renderSearch() {
  var content = divWithClass("search");

  content.append(_renderSearchForm());
  return content;
}
function keyOrderForArticle(art) {
  function mergeOrder(list, set, keyOrder) {
    keyOrder.forEach(function(k) {
      if (set.has(k))
        return;
      list.push(k);
      set.add(k);
    });
  }

  var tradOrder = [];
  var order = art.order;
  if (!order) order = [];

  var orderSet = new Set(order);
  mergeOrder(order, orderSet, tradOrder);

  var alphaKeys = _.keys(art);
  alphaKeys.sort();
  mergeOrder(order, orderSet, alphaKeys);

  return order;
}

function renderArticle(art) {
  var content = tagWithClass('article');
  var ignoredKeys = new Set(['title']);

  var order = keyOrderForArticle(art);

  var editMap = [];

  var sharedCommit = {
    art: art,
    callback: (function() {
      $(this).hide();

      var art = window.ARTICLE;

      // Modify
      editMap.forEach(function(mapping) {
        mapping();
      });

      console.log(JSON.stringify(art, null, 4));

      runJSON('POST', 'api/item', JSON.stringify(art), function(resp) {
        var status = resp.status;
        var content;
        if (status === 200) {
          location.reload(true);
        }
        else {
          debugger;
          alert("Error");
        }
      });

    }),
  };
  var commitButton = $('
Commit
');
  commitButton.hide();
  commitButton.on('click', function() {
    sharedCommit.callback.bind(this)();
  });

  // Heading
  var h1 = $('

');
  h1.text(art.title);
  h1.append(commitButton);
  content.append(h1);

  marked.setOptions({
    renderer: new marked.Renderer(),
    gfm: true,
    tables: true,
    breaks: false,
    pedantic: false,
    sanitize: true,
    smartLists: true,
    smartypants: false,
  });

  order.forEach(function(k) {
    if (ignoredKeys.has(k)) return;

    var value = art[k];
    if (value == null) return;

    var section = tagWithClass('section');

    if (_.isObject(value)) {
      var kind = value.kind;

      // This shared object exposes a callback that can be set below
      var shared = {
        modeString: 'read',
        callback: function() { } };

      if (value.hasOwnProperty('title')) {
        var h2 = $('

');
        h2.text(value.title);
        h2.append(makeEditButton(function(editButton) {
          console.log(shared.modeString);
          if (shared.modeString === 'read')
            shared.modeString = 'edit';
          else if (shared.modeString === 'edit')
            shared.modeString = 'commit';
          shared.editButton = $(this);
          shared.callback();
        }));
        section.append(h2);
      }

      if (value.hasOwnProperty('val')) {
        var innerVal = value.val;
        if (_.isString(innerVal)) {
          /*editMap.push((function() {
            return function(_art) {
              value.val =
            };
          })())*/

          var stuff = divWithClass('paras');
          var cm_mode = 'plain';
          if (kind === 'md') {
            // Markdown
            stuff.html(marked(innerVal));
            cm_mode = 'markdown';
          }
          else if (kind === 'txt') {
            // Plain text
            stuff.text(innerVal);;
          }
          else {
            // Something else
            cm_mode = 'javascript';
            stuff = tagWithClass('pre');
            stuff.addClass('padded');
            stuff.addClass('cm-s-cmtn');
            CodeMirror.runMode(innerVal, cm_mode, stuff[0]);
          }
          section.append(stuff);

          var editor = divWithClass('editor');
          editor.hide();
          var cm = makeCodeMirror(editor, cm_mode, '');
          section.append(editor);

          shared.value = innerVal;
          shared.callback = function() {

            if (shared.modeString === 'edit') {
              // Edit mode
              stuff.hide();
              editor.show();
              cm.setValue(shared.value);
              codeMirrorGoEnd(cm);
              cm.focus();

              shared.editButton.text('Commit');
              shared.editButton.removeClass('edit_button').addClass('important_button')

              shared.editButton.hide();
              commitButton.show();

              editMap.push(function() {
                value.val = cm.getValue();
              });
            }
          };
        }
      }
    }

    content.append(section);
  });

  return content;
}
function renderFunction() {
  var art = tagWithClass('article', 'func');
  return art;
}
function renderArticle404(path) {
  return renderNewPage(path);
  return renderArticle({
    title: 'Uh oh',
    order: ["foo", "baz", "bar"],
    foo: { title: "Foo", val: "*Hello*", kind: "md" },
    bar: { title: "Foo", val: "*Hello*", kind: "txt" },
    baz: { title: "Foo", val: "function foo()", kind: "js" },
  });

  var content = divWithClass('article404');
  content.text("Page not found");
  return content;
}
function renderNewPage(path) {
  var content = divWithClass('new_page');

  var titleInput = $("<input name='title' type='text'>");
  content.append(titleInput);

  function _makeTSelectInput(name) {
    return $("<option>").text(name);
  }

  var templateSelect = $("<select name='template'>");
  templateSelect.append([
    _makeTSelectInput("Article"),
    _makeTSelectInput("Function"),
  ])
  content.append(templateSelect);

  var editorToolbar = $('
');
  var createButton = $('

');
  createButton.text("Create");
  editorToolbar.append(createButton);
  content.append(editorToolbar);

  createButton.on('click', function() {

    var art = {
        title: path,
        order: ["description", "impl", "tests"],
        description: { title: "About", val: "*This is a new page, add some content*", kind: "md" },
        // ccc: { title: "CCC", val: "*Hello*", kind: "txt" },
        impl: { title: "Implementation", val: "function foo() {\n}\n", kind: "js" },
        tests: { title: "Implementation", val: "function test1() {\n}\n", kind: "js" },
    };
    runJSON('POST', 'api/create-from-template', JSON.stringify(art), function(resp) {
      var status = resp.status;
      var content;
      if (status === 200) {
        location.reload(true);
      }
      else {
        debugger;
        alert("Error");
      }
    });

  });

  return content;
}
function editSection() {

}

function viewForArticle(path) {
  // Get the article
  runJSON('GET', 'api/find-latest', {
    'title': path,
  }, function(resp) {
    var status = resp.status;
    var content;
    if (status === 200) {
      var art = resp.responseJSON.content;
      console.log(art);
      window.ARTICLE = art;
      content = renderArticle(art);
    }
    else if (status === 404)
      content = renderArticle404(path);
    else {
      debugger;
      alert("Error");
    }
    $("main #innerA").append(content);
  });

  return [];
}

function route() {
  var path = window.location.pathname;
  if (path.indexOf('/') === 0)
    path = path.slice(1);

  var content = null;
  if (path === '') {
    content = renderHome();
  }
  else if (path === 'search') {
    content = renderSearch();
  }
  else {
    content = viewForArticle(path);
  }

  $("main #innerA").append(content);
}

$(function() {
  route();
})
<commit_msg>[autopublish]<commit_after>// --- Util ---
function makeEditButton(onclick) {
  return $('
Edit
').on('click', onclick);
}
function makeURL(path) {
  // return '/' + path;
  return 'https://bhasa.herokuapp.com/' + path;
}
function runJSON(method, path, query, callback) {
  console.log(query);

  var settings = {
    'method': method,
    'url': makeURL(path),
    'data': query,
    // 'dataType': 'json',
    'complete': callback,
  };

  if (method !== 'GET' && method !== 'HEAD')
    settings.contentType = 'application/json';

  $.ajax(settings);
}
function divWithClass(cl) {
  var div = $("
");
  if (cl !== null)
    div.addClass(cl);
  return div;
}
function tagWithClass(name, cl) {
  var el = $("<" + name + ">");
  if (cl !== null)
    el.addClass(cl);
  return el;
}
function codeMirrorGoEnd(cm) {
  cm.execCommand("goDocEnd"); // try once
  setTimeout(function() { cm.execCommand("goDocEnd"); }, 1); // try again
}
function makeCodeMirror(el, mode, txt) {
  console.log(el.get(0));
  var cm = CodeMirror(el[0], {
    viewportMargin: Infinity,
    mode: mode,
    tabSize: 2,
    // lineNumbers: true,
    autofocus: true,
    lineWrapping: true,
    theme: 'cmtn',
    value: txt,
  });
  codeMirrorGoEnd(cm);

  return cm;
}

// --- Rendering ---
function _renderSearchResult(r) {
  var result = divWithClass("pl_item");
  result.addClass('noselect');
  result.text(r.title);
  return result;
}
function _renderSearchForm() {
  var search = $('<input class="searchfield" type="search" placeholder="search e.g. map()">');

  var search_results = $('
');
  search_results.addClass("pl_list")
  search_results.append(_renderSearchResult({ title: "Hello" }));
  search_results.append(_renderSearchResult({ title: "Hello" }));

  var doSearch = _.debounce(function() {
    var query = search.val();
    runJSON('GET', 'api/search', { 'q': query }, function(data) {
      console.log(data);
      data = data.responseJSON;
      console.log(data);

      var rs = data.results;
      var els = [];
      rs.forEach(function(r) {
        els.push(_renderSearchResult(r));
      });

      search_results.empty();
      search_results.append(els);

    }, 'json');
  }, 500);

  search.on('input', function() {
    doSearch();
  });

  return [search, search_results];
}
function renderHome() {
  var content = divWithClass("home");

  var tagline = tagWithClass("p", "tagline");
  tagline.html("bhasa is the programming language that anyone can edit.");
  content.append(tagline);

  content.append(_renderSearchForm());
  return content;
}
function renderSearch() {
  var content = divWithClass("search");

  content.append(_renderSearchForm());
  return content;
}
function keyOrderForArticle(art) {
  function mergeOrder(list, set, keyOrder) {
    keyOrder.forEach(function(k) {
      if (set.has(k))
        return;
      list.push(k);
      set.add(k);
    });
  }

  var tradOrder = [];
  var order = art.order;
  if (!order) order = [];

  var orderSet = new Set(order);
  mergeOrder(order, orderSet, tradOrder);

  var alphaKeys = _.keys(art);
  alphaKeys.sort();
  mergeOrder(order, orderSet, alphaKeys);

  return order;
}

function renderArticle(art) {
  var content = tagWithClass('article');
  var ignoredKeys = new Set(['title']);

  var order = keyOrderForArticle(art);

  var editMap = [];

  var sharedCommit = {
    art: art,
    callback: (function() {
      $(this).hide();

      var art = window.ARTICLE;

      // Modify
      editMap.forEach(function(mapping) {
        mapping();
      });

      console.log(JSON.stringify(art, null, 4));

      runJSON('POST', 'api/item', JSON.stringify(art), function(resp) {
        var status = resp.status;
        var content;
        if (status === 200) {
          location.reload(true);
        }
        else {
          debugger;
          alert("Error");
        }
      });

    }),
  };
  var commitButton = $('
Commit
');
  commitButton.hide();
  commitButton.on('click', function() {
    sharedCommit.callback.bind(this)();
  });

  // Heading
  var h1 = $('

');
  h1.text(art.title);
  h1.append(commitButton);
  content.append(h1);

  marked.setOptions({
    renderer: new marked.Renderer(),
    gfm: true,
    tables: true,
    breaks: false,
    pedantic: false,
    sanitize: true,
    smartLists: true,
    smartypants: false,
  });

  order.forEach(function(k) {
    if (ignoredKeys.has(k)) return;

    var value = art[k];
    if (value == null) return;

    var section = tagWithClass('section');

    if (_.isObject(value)) {
      var kind = value.kind;

      // This shared object exposes a callback that can be set below
      var shared = {
        modeString: 'read',
        callback: function() { } };

      if (value.hasOwnProperty('title')) {
        var h2 = $('

');
        h2.text(value.title);
        h2.append(makeEditButton(function(editButton) {
          console.log(shared.modeString);
          if (shared.modeString === 'read')
            shared.modeString = 'edit';
          else if (shared.modeString === 'edit')
            shared.modeString = 'commit';
          shared.editButton = $(this);
          shared.callback();
        }));
        section.append(h2);
      }

      if (value.hasOwnProperty('val')) {
        var innerVal = value.val;
        if (_.isString(innerVal)) {
          /*editMap.push((function() {
            return function(_art) {
              value.val =
            };
          })())*/

          var stuff = divWithClass('paras');
          var cm_mode = 'plain';
          if (kind === 'md') {
            // Markdown
            stuff.html(marked(innerVal));
            cm_mode = 'markdown';
          }
          else if (kind === 'txt') {
            // Plain text
            stuff.text(innerVal);;
          }
          else {
            // Something else
            cm_mode = 'javascript';
            stuff = tagWithClass('pre');
            stuff.addClass('padded');
            stuff.addClass('cm-s-cmtn');
            CodeMirror.runMode(innerVal, cm_mode, stuff[0]);
          }
          section.append(stuff);

          var editor = divWithClass('editor');
          editor.hide();
          var cm = makeCodeMirror(editor, cm_mode, '');
          section.append(editor);

          shared.value = innerVal;
          shared.callback = function() {

            if (shared.modeString === 'edit') {
              // Edit mode
              stuff.hide();
              editor.show();
              cm.setValue(shared.value);
              codeMirrorGoEnd(cm);
              cm.focus();

              shared.editButton.text('Commit');
              shared.editButton.removeClass('edit_button').addClass('important_button')

              shared.editButton.hide();
              commitButton.show();

              editMap.push(function() {
                value.val = cm.getValue();
              });
            }
          };
        }
      }
    }

    content.append(section);
  });

  return content;
}
function renderFunction() {
  var art = tagWithClass('article', 'func');
  return art;
}
function renderArticle404(path) {
  return renderNewPage(path);
  return renderArticle({
    title: 'Uh oh',
    order: ["foo", "baz", "bar"],
    foo: { title: "Foo", val: "*Hello*", kind: "md" },
    bar: { title: "Foo", val: "*Hello*", kind: "txt" },
    baz: { title: "Foo", val: "function foo()", kind: "js" },
  });

  var content = divWithClass('article404');
  content.text("Page not found");
  return content;
}
function renderNewPage(path) {
  var content = divWithClass('new_page');

  var titleInput = $("<input name='title' type='text'>");
  content.append(titleInput);

  function _makeTSelectInput(name) {
    return $("<option>").text(name);
  }

  var templateSelect = $("<select name='template'>");
  templateSelect.append([
    _makeTSelectInput("Article"),
    _makeTSelectInput("Function"),
  ])
  content.append(templateSelect);

  var editorToolbar = $('
');
  var createButton = $('

');
  createButton.text("Create");
  editorToolbar.append(createButton);
  content.append(editorToolbar);

  createButton.on('click', function() {

    var art = {
        title: path,
        order: ["description", "impl", "tests"],
        description: { title: "About", val: "*This is a new page, add some content*", kind: "md" },
        // ccc: { title: "CCC", val: "*Hello*", kind: "txt" },
        impl: { title: "Implementation", val: "function foo() {\n}\n", kind: "js" },
        tests: { title: "Tests", val: "function test1() {\n}\n", kind: "js" },
    };
    runJSON('POST', 'api/create-from-template', JSON.stringify(art), function(resp) {
      var status = resp.status;
      var content;
      if (status === 200) {
        location.reload(true);
      }
      else {
        debugger;
        alert("Error");
      }
    });

  });

  return content;
}
function editSection() {

}

function viewForArticle(path) {
  // Get the article
  runJSON('GET', 'api/find-latest', {
    'title': path,
  }, function(resp) {
    var status = resp.status;
    var content;
    if (status === 200) {
      var art = resp.responseJSON.content;
      console.log(art);
      window.ARTICLE = art;
      content = renderArticle(art);
    }
    else if (status === 404)
      content = renderArticle404(path);
    else {
      debugger;
      alert("Error");
    }
    $("main #innerA").append(content);
  });

  return [];
}

function route() {
  var path = window.location.pathname;
  if (path.indexOf('/') === 0)
    path = path.slice(1);

  var content = null;
  if (path === '') {
    content = renderHome();
  }
  else if (path === 'search') {
    content = renderSearch();
  }
  else {
    content = viewForArticle(path);
  }

  $("main #innerA").append(content);
}

$(function() {
  route();
})
<|endoftext|>


<commit_before>// Library of contracts and functors.
// TODO: cache typing judgements on functions in a WeakMap.
var CT = (function (){
  var call = Function.prototype.call;
  var slice = call.bind([].slice);
  var getClassName = call.bind({}.toString);
  var create = Object.create.bind(Object);
  var gpo = Object.getPrototypeOf.bind(Object);

  var install = function (global) {
    for (var i in CT) {
      global[i] = CT[i];
    }
  };

  // A contract that allows anything
  var any = function (x) { return x; };

  // Contracts for special values
  var undef = function (x) {
    if (x !== void 0) { throw new TypeError("Expected undefined."); }
    return x;
  };
  var nul = function (x) {
    if (x !== null) { throw new TypeError("Expected null."); }
    return x;
  };
  var nan = function (x) {
    if (x === x) { throw new TypeError("Expected NaN."); }
    return x;
  };

  // Creates a contract that tests the [[Class]]
  // internal property of the object.
  var classOf = function (s) {
    return function (v) {
      if (getClassName(v) !== "[object " + s + "]") {
        throw new TypeError("Expected " + s);
      }
      return v;
    };
  };

  var array = classOf("Array");
  var date = classOf("Date");
  var regexp = classOf("RegExp");

  // Creates a contract for a value of type s
  var typeOf = function (s) {
    return function (v) {
      if (typeof v !== s) {
        throw new TypeError("Expected a" + (s === "object" ? "n" : "") + s + ".");
      }
      return v;
    };
  };

  var func = typeOf("function");
  var string = typeOf("string");
  var object = typeOf("object");
  var boolean = typeOf("boolean");
  var number = typeOf("number");

  // Creates a contract for an object inheriting from ctor
  var instanceOf = function (ctor) {
    return function (inst) {
      if (!(inst instanceof ctor)) {
        throw new TypeError("Expected an instance of " + ctor);
      }
      return inst;
    };
  }

  // Asserts n is a signed 32-bit number
  var int32 = function (n) {
    if ((n | 0) !== n) {
      throw new TypeError("Expected a 32-bit integer.");
    }
    return n;
  };

  // Asserts int32 and nonnegative
  var nat32 = function (n) {
    if ((n | 0) !== n || n < 0) {
      throw new TypeError("Expected a 32-bit natural.");
    }
    return n;
  };

  // Array.prototype.map passes three params:
  // the element, the index, and the array.
  // The mapClean function only passes the element.
  Object.defineProperty(Array.prototype, "mapClean", {
    writable: true,
    enumerable: false,
    configurable: true,
    value: function (f) {
      return this.map(function (x) { return f(x); });
    }
  });

  // Creates a contract for an array whose
  // elements all satisfy the contract c
  var arrayOf = function (c) {
    func(c);
    return function (a) {
      return array(a).mapClean(c);
    };
  };

  // Creates a contract for an object whose
  // enumerable properties all satisfy the contract c
  var objectOf = function (c) {
    func(c);
    return function (o) {
      object(o);
      var result = create(gpo(o));
      for (i in o) {
        result[i] = c(o[i]);
      }
      return result;
    };
  };

  // Given an array of contracts, creates a contract for
  // an array whose elements satisfy the respective contracts:
  // the product of the given contracts, indexed by numbers
  var prodn = function (cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (args) {
      array(args);
      if (args.length !== len) {
        throw new TypeError("Expected " + len + " arguments");
      }
      var result = [];
      for (var i = 0; i < len; ++i) {
        // Apply each contract to the
        // corresponding argument.
        result[i] = cs[i](args[i]);
      }
      return result;
    };
  };

  // Given an object whose enumerable properties are contracts,
  // creates a contract for an object whose enumerable properties
  // satisfy the respective contracts: the product of the given
  // contracts, indexed by strings.
  var prods = function (cs) {
    object(cs);
    for (var i in cs) {
      func(cs[i]);
    }
    return function (x) {
      object(x);
      var result = create(gpo(x));
      for (var i in cs) {
        result[i] = cs[i](x[i]);
      }
      return result;
    };
  };

  // Given an array of contracts, creates a contract for a
  // 2-element array where item 0 is an index and item 1
  // is a value satisfying the contract at that index
  // in the array: the coproduct of the given contracts,
  // tagged by numbers.
  var coprodn = function (cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (choice) {
      array(choice);
      nat32(choice[0]);
      if (choice.length !== 2) {
        throw new TypeError("Expected [nat32, any].");
      }
      if (choice[0] >= len ) {
        throw new TypeError("Tag out of range.")
      }
      return [choice[0], cs[choice[0]](choice[1])];
    };
  };

  // Given an object of contracts, creates a contract for a
  // 2-element array where item 0 is a property name and item 1
  // is a value satisfying the contract at that property name
  // in the object: the coproduct of the given contracts,
  // tagged by strings.
  var coprods = function (cs) {
    objectOf(func)(cs);
    return function (choice) {
      array(choice);
      string(choice[0]);
      if (choice.length !== 2) {
        throw new TypeError("Expected [string, any].");
      }
      if (!cs.hasOwnProperty(choice[0])) {
        throw new TypeError("Unknown tag.")
      }
      return [choice[0], cs[choice[0]](choice[1])];
    };
  };

  // Given an array of contracts, apply all of them.
  // This is the product in the category of sets and inclusions.
  var intersect = function (cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (x) {
      var result = x;
      for (var i = 0; i < len; ++i) {
        result = cs[i](result);
      }
      return result;
    };
  };

  // Given an array of contracts, succeed if any succeeds.
  // This is the coproduct in the category of sets and inclusions.
  var union = function(cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (x) {
      for (var i = 0; i < len; ++i) {
        try {
          return cs[i](x);
        } catch (e) { }
      }
      throw new TypeError("No match among unioned types.")
    };
  };

  // Given an array of functions, returns a contract
  // for those arrays where the elements all map to the
  // same value under the given functions, e.g. given
  // [f, g], we get a contract for those [x, y] for which
  // f(x) === g(y): the pullback of the functions f and g.
  var pbn = function (fs) {
    var c = prodn(fs);
    var len = fs.length;
    return function (args) {
      array(args);
      var result = c(args);
      for (var i = 1; i < len; ++i) {
        if (result[i] !== result[0]) {
          throw new TypeError("Failed to match pullback constraint in position " + i);
        }
      }
      return args;
    };
  };

  // TODO: pushouts

  // Helper for the hom functor below.
  // hom(a,b).self(contractForThis)(function method(){...})(args...)
  var self = function (c) {
    func(c);
    var homab = this;
    return function (method) {
      // homab(method) checks the pre/post conditions on method
      method = homab(method);
      var result = function (varArgs) {
        // c(this) checks that it is being invoked on the right kind of object
        return method.apply(c(this), arguments);
      };
      result.toString = function () { return method.toString(); }
      return result;
    };
  };

  // Creates a contract for a function whose inputs and output
  // satisfy the given contracts.
  var hom = function hom(/* input1, ..., inputn, output */) {
    var precond = prodn(arrayOf(func)(slice(arguments, 0, arguments.length - 1)));
    var postcond = func(arguments[arguments.length - 1]);
    var result = function (middle) {
      func(middle);
      var result = function (varArgs) {
        return postcond(
               middle.apply(this,
               precond(slice(arguments))));
      };
      result.toString = (function (str) {
        return function () { return str + "/* guarded */"; };
      })("" + middle);
      return result;
    };
    result.self = self.bind(result);
    return result;
  };

  // Using prods and hom.self to define an interface:
  // var Fooable = prods({
  //   foo: hom(int32, string).self(Fooable);
  // });

  // Creates a memoized contract for a subcontract of string
  // As a functor, creates a memoized version of a
  // function f:str -> any
  var memo = function (c) {
    c = hom(string, any)(c);
    var memo = {};
    return function (s) {
      // The "$" prefix avoids issues with
      // magic properties like __proto__.
      var key = "$" + string(s);
      if (key in memo) {
        return memo[key];
      }
      return memo[key] = c(s);
    };
  };

  var CT = {
    allOf: intersect,
    any: any,
    anyOf: union,
    array: array,
    arrayOf: arrayOf,
    boolean: boolean,
    coprodn: coprodn,
    coprods: coprods,
    date: date,
    func: func,
    hom: hom,
    id: any,
    instanceOf: instanceOf,
    int32: int32,
    intersect: intersect,
    memo: memo,
    nat32: nat32,
    number: number,
    object: object,
    objectOf: objectOf,
    pbn: pbn,
    prodn: prodn,
    prods: prods,
    regexp: regexp,
    string: string,
    union: union,

    install: install
  };

  return CT;
})();
<commit_msg>Insert missing space<commit_after>// Library of contracts and functors.
// TODO: cache typing judgements on functions in a WeakMap.
var CT = (function (){
  var call = Function.prototype.call;
  var slice = call.bind([].slice);
  var getClassName = call.bind({}.toString);
  var create = Object.create.bind(Object);
  var gpo = Object.getPrototypeOf.bind(Object);

  var install = function (global) {
    for (var i in CT) {
      global[i] = CT[i];
    }
  };

  // A contract that allows anything
  var any = function (x) { return x; };

  // Contracts for special values
  var undef = function (x) {
    if (x !== void 0) { throw new TypeError("Expected undefined."); }
    return x;
  };
  var nul = function (x) {
    if (x !== null) { throw new TypeError("Expected null."); }
    return x;
  };
  var nan = function (x) {
    if (x === x) { throw new TypeError("Expected NaN."); }
    return x;
  };

  // Creates a contract that tests the [[Class]]
  // internal property of the object.
  var classOf = function (s) {
    return function (v) {
      if (getClassName(v) !== "[object " + s + "]") {
        throw new TypeError("Expected " + s);
      }
      return v;
    };
  };

  var array = classOf("Array");
  var date = classOf("Date");
  var regexp = classOf("RegExp");

  // Creates a contract for a value of type s
  var typeOf = function (s) {
    return function (v) {
      if (typeof v !== s) {
        throw new TypeError("Expected a " + (s === "object" ? "n" : "") + s + ".");
      }
      return v;
    };
  };

  var func = typeOf("function");
  var string = typeOf("string");
  var object = typeOf("object");
  var boolean = typeOf("boolean");
  var number = typeOf("number");

  // Creates a contract for an object inheriting from ctor
  var instanceOf = function (ctor) {
    return function (inst) {
      if (!(inst instanceof ctor)) {
        throw new TypeError("Expected an instance of " + ctor);
      }
      return inst;
    };
  }

  // Asserts n is a signed 32-bit number
  var int32 = function (n) {
    if ((n | 0) !== n) {
      throw new TypeError("Expected a 32-bit integer.");
    }
    return n;
  };

  // Asserts int32 and nonnegative
  var nat32 = function (n) {
    if ((n | 0) !== n || n < 0) {
      throw new TypeError("Expected a 32-bit natural.");
    }
    return n;
  };

  // Array.prototype.map passes three params:
  // the element, the index, and the array.
  // The mapClean function only passes the element.
  Object.defineProperty(Array.prototype, "mapClean", {
    writable: true,
    enumerable: false,
    configurable: true,
    value: function (f) {
      return this.map(function (x) { return f(x); });
    }
  });

  // Creates a contract for an array whose
  // elements all satisfy the contract c
  var arrayOf = function (c) {
    func(c);
    return function (a) {
      return array(a).mapClean(c);
    };
  };

  // Creates a contract for an object whose
  // enumerable properties all satisfy the contract c
  var objectOf = function (c) {
    func(c);
    return function (o) {
      object(o);
      var result = create(gpo(o));
      for (i in o) {
        result[i] = c(o[i]);
      }
      return result;
    };
  };

  // Given an array of contracts, creates a contract for
  // an array whose elements satisfy the respective contracts:
  // the product of the given contracts, indexed by numbers
  var prodn = function (cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (args) {
      array(args);
      if (args.length !== len) {
        throw new TypeError("Expected " + len + " arguments");
      }
      var result = [];
      for (var i = 0; i < len; ++i) {
        // Apply each contract to the
        // corresponding argument.
        result[i] = cs[i](args[i]);
      }
      return result;
    };
  };

  // Given an object whose enumerable properties are contracts,
  // creates a contract for an object whose enumerable properties
  // satisfy the respective contracts: the product of the given
  // contracts, indexed by strings.
  var prods = function (cs) {
    object(cs);
    for (var i in cs) {
      func(cs[i]);
    }
    return function (x) {
      object(x);
      var result = create(gpo(x));
      for (var i in cs) {
        result[i] = cs[i](x[i]);
      }
      return result;
    };
  };

  // Given an array of contracts, creates a contract for a
  // 2-element array where item 0 is an index and item 1
  // is a value satisfying the contract at that index
  // in the array: the coproduct of the given contracts,
  // tagged by numbers.
  var coprodn = function (cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (choice) {
      array(choice);
      nat32(choice[0]);
      if (choice.length !== 2) {
        throw new TypeError("Expected [nat32, any].");
      }
      if (choice[0] >= len ) {
        throw new TypeError("Tag out of range.")
      }
      return [choice[0], cs[choice[0]](choice[1])];
    };
  };

  // Given an object of contracts, creates a contract for a
  // 2-element array where item 0 is a property name and item 1
  // is a value satisfying the contract at that property name
  // in the object: the coproduct of the given contracts,
  // tagged by strings.
  var coprods = function (cs) {
    objectOf(func)(cs);
    return function (choice) {
      array(choice);
      string(choice[0]);
      if (choice.length !== 2) {
        throw new TypeError("Expected [string, any].");
      }
      if (!cs.hasOwnProperty(choice[0])) {
        throw new TypeError("Unknown tag.")
      }
      return [choice[0], cs[choice[0]](choice[1])];
    };
  };

  // Given an array of contracts, apply all of them.
  // This is the product in the category of sets and inclusions.
  var intersect = function (cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (x) {
      var result = x;
      for (var i = 0; i < len; ++i) {
        result = cs[i](result);
      }
      return result;
    };
  };

  // Given an array of contracts, succeed if any succeeds.
  // This is the coproduct in the category of sets and inclusions.
  var union = function(cs) {
    arrayOf(func)(cs);
    var len = cs.length;
    return function (x) {
      for (var i = 0; i < len; ++i) {
        try {
          return cs[i](x);
        } catch (e) { }
      }
      throw new TypeError("No match among unioned types.")
    };
  };

  // Given an array of functions, returns a contract
  // for those arrays where the elements all map to the
  // same value under the given functions, e.g. given
  // [f, g], we get a contract for those [x, y] for which
  // f(x) === g(y): the pullback of the functions f and g.
  var pbn = function (fs) {
    var c = prodn(fs);
    var len = fs.length;
    return function (args) {
      array(args);
      var result = c(args);
      for (var i = 1; i < len; ++i) {
        if (result[i] !== result[0]) {
          throw new TypeError("Failed to match pullback constraint in position " + i);
        }
      }
      return args;
    };
  };

  // TODO: pushouts

  // Helper for the hom functor below.
  // hom(a,b).self(contractForThis)(function method(){...})(args...)
  var self = function (c) {
    func(c);
    var homab = this;
    return function (method) {
      // homab(method) checks the pre/post conditions on method
      method = homab(method);
      var result = function (varArgs) {
        // c(this) checks that it is being invoked on the right kind of object
        return method.apply(c(this), arguments);
      };
      result.toString = function () { return method.toString(); }
      return result;
    };
  };

  // Creates a contract for a function whose inputs and output
  // satisfy the given contracts.
  var hom = function hom(/* input1, ..., inputn, output */) {
    var precond = prodn(arrayOf(func)(slice(arguments, 0, arguments.length - 1)));
    var postcond = func(arguments[arguments.length - 1]);
    var result = function (middle) {
      func(middle);
      var result = function (varArgs) {
        return postcond(
               middle.apply(this,
               precond(slice(arguments))));
      };
      result.toString = (function (str) {
        return function () { return str + "/* guarded */"; };
      })("" + middle);
      return result;
    };
    result.self = self.bind(result);
    return result;
  };

  // Using prods and hom.self to define an interface:
  // var Fooable = prods({
  //   foo: hom(int32, string).self(Fooable);
  // });

  // Creates a memoized contract for a subcontract of string
  // As a functor, creates a memoized version of a
  // function f:str -> any
  var memo = function (c) {
    c = hom(string, any)(c);
    var memo = {};
    return function (s) {
      // The "$" prefix avoids issues with
      // magic properties like __proto__.
      var key = "$" + string(s);
      if (key in memo) {
        return memo[key];
      }
      return memo[key] = c(s);
    };
  };

  var CT = {
    allOf: intersect,
    any: any,
    anyOf: union,
    array: array,
    arrayOf: arrayOf,
    boolean: boolean,
    coprodn: coprodn,
    coprods: coprods,
    date: date,
    func: func,
    hom: hom,
    id: any,
    instanceOf: instanceOf,
    int32: int32,
    intersect: intersect,
    memo: memo,
    nat32: nat32,
    number: number,
    object: object,
    objectOf: objectOf,
    pbn: pbn,
    prodn: prodn,
    prods: prods,
    regexp: regexp,
    string: string,
    union: union,

    install: install
  };

  return CT;
})();
<|endoftext|>


<commit_before>var spawn = require('child_process').spawn
var Queue = require('firebase-queue'),
    Firebase = require('firebase')

if (process.argv.length < 4) {
  console.log("usage: node worker.js firebaseUrl command [arg1, arg2, ...]")
  console.log("  For authentication, set FIREBASE_TOKEN environment variable.")
  process.exit()
}
var queueUrl = process.argv[2]
var cmdArgs = process.argv.slice(3)

var queueRef = new Firebase(queueUrl)
var start = function () {
  // presence of worker
  queueRef.root().child('.info/connected').on('value', function(snapshot) {
    if (snapshot.val()) {
      var workerRef = queueRef.child("workers").push({hostname: require('os').hostname()})
      workerRef.onDisconnect().remove();
    }
  })
  console.log("WORKER: waiting a runner pushed a task...")
  var queue = new Queue(queueRef, function (data, progress, resolve, reject) {
    // Read and process task data
    var bufferRef = new Firebase(data.bufferUrl)
    var log = function (msg) {
      bufferRef.child('log').push(msg.toString())
      process.stdout.write(msg.toString())
    }
    log("WORKER: start command: " + cmdArgs + "\n\n")
    var env = Object.create(process.env)
    for (var name in data.env) {
      env[name] = data.env[name]
    }
    var cmd
    if (os.platform() === 'win32') {
      cmd = spawn(process.env.comspec, ['/c'].concat(cmdArgs), { env: env })
    } else {
      cmd = spawn(cmdArgs[0], cmdArgs.slice(1), { env: env })
    }
    cmd.stdout.on('data', log)
    cmd.stderr.on('data', log)
    cmd.on('error', function (err) { reject(err) })
    cmd.on('close', function (code) {
      if (code === 0) {
        log("\nWORKER: command completed.\n\n")
        resolve()
      } else {
        log("\nWORKER: command failed.\n\n")
        reject("exit code: " + code)
      }
      console.log("WORKER: waiting a runner pushed another task...")
    })
  })
  // graceful shutdown
  process.on('SIGINT', function () {
    console.log('WORKER: Starting queue shutdown...');
    queue.shutdown().then(function () {
      console.log('WORKER: Finished queue shutdown.');
      process.exit(1)
    });
  })
}

if (process.env.FIREBASE_TOKEN) {
  queueRef.authWithCustomToken(process.env.FIREBASE_TOKEN, function (error, result) {
    if (error) {
      console.log("WORKER: Authentication Failed:", error)
    } else {
      console.log("WORKER: Authentication succeeded!")
      start()
    }
  })
  delete process.env.FIREBASE_TOKEN
} else {
  start()
}
<commit_msg>require('os')<commit_after>var os = require('os')
var spawn = require('child_process').spawn
var Queue = require('firebase-queue'),
    Firebase = require('firebase')

if (process.argv.length < 4) {
  console.log("usage: node worker.js firebaseUrl command [arg1, arg2, ...]")
  console.log("  For authentication, set FIREBASE_TOKEN environment variable.")
  process.exit()
}
var queueUrl = process.argv[2]
var cmdArgs = process.argv.slice(3)

var queueRef = new Firebase(queueUrl)
var start = function () {
  // presence of worker
  queueRef.root().child('.info/connected').on('value', function(snapshot) {
    if (snapshot.val()) {
      var workerRef = queueRef.child("workers").push({hostname: require('os').hostname()})
      workerRef.onDisconnect().remove();
    }
  })
  console.log("WORKER: waiting a runner pushed a task...")
  var queue = new Queue(queueRef, function (data, progress, resolve, reject) {
    // Read and process task data
    var bufferRef = new Firebase(data.bufferUrl)
    var log = function (msg) {
      bufferRef.child('log').push(msg.toString())
      process.stdout.write(msg.toString())
    }
    log("WORKER: start command: " + cmdArgs + "\n\n")
    var env = Object.create(process.env)
    for (var name in data.env) {
      env[name] = data.env[name]
    }
    var cmd
    if (os.platform() === 'win32') {
      cmd = spawn(process.env.comspec, ['/c'].concat(cmdArgs), { env: env })
    } else {
      cmd = spawn(cmdArgs[0], cmdArgs.slice(1), { env: env })
    }
    cmd.stdout.on('data', log)
    cmd.stderr.on('data', log)
    cmd.on('error', function (err) { reject(err) })
    cmd.on('close', function (code) {
      if (code === 0) {
        log("\nWORKER: command completed.\n\n")
        resolve()
      } else {
        log("\nWORKER: command failed.\n\n")
        reject("exit code: " + code)
      }
      console.log("WORKER: waiting a runner pushed another task...")
    })
  })
  // graceful shutdown
  process.on('SIGINT', function () {
    console.log('WORKER: Starting queue shutdown...');
    queue.shutdown().then(function () {
      console.log('WORKER: Finished queue shutdown.');
      process.exit(1)
    });
  })
}

if (process.env.FIREBASE_TOKEN) {
  queueRef.authWithCustomToken(process.env.FIREBASE_TOKEN, function (error, result) {
    if (error) {
      console.log("WORKER: Authentication Failed:", error)
    } else {
      console.log("WORKER: Authentication succeeded!")
      start()
    }
  })
  delete process.env.FIREBASE_TOKEN
} else {
  start()
}
<|endoftext|>