<commit_before>'use strict';

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

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

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

module.exports = class mercado extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'mercado',
            'name': 'Mercado Bitcoin',
            'countries': ['BR'], // Brazil
            'rateLimit': 1000,
            'version': 'v3',
            'has': {
                'CORS': true,
                'createMarketOrder': true,
                'fetchOrder': true,
                'withdraw': true,
                'fetchOHLCV': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
                'fetchTicker': true,
                'fetchTickers': true,
            },
            'timeframes': {
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '1w': '1w',
                '2w': '2w',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg',
                'api': {
                    'public': 'https://www.mercadobitcoin.net/api',
                    'private': 'https://www.mercadobitcoin.net/tapi',
                    'v4Public': 'https://www.mercadobitcoin.com.br/v4',
                },
                'www': 'https://www.mercadobitcoin.com.br',
                'doc': [
                    'https://www.mercadobitcoin.com.br/api-doc',
                    'https://www.mercadobitcoin.com.br/trade-api',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        '{coin}/orderbook/', // last slash critical
                        '{coin}/ticker/',
                        '{coin}/trades/',
                        '{coin}/trades/{from}/',
                        '{coin}/trades/{from}/{to}',
                        '{coin}/day-summary/{year}/{month}/{day}/',
                    ],
                },
                'private': {
                    'post': [
                        'cancel_order',
                        'get_account_info',
                        'get_order',
                        'get_withdrawal',
                        'list_system_messages',
                        'list_orders',
                        'list_orderbook',
                        'place_buy_order',
                        'place_sell_order',
                        'place_market_buy_order',
                        'place_market_sell_order',
                        'withdraw_coin',
                    ],
                },
                'v4Public': {
                    'get': [
                        '{coin}/candle/',
                    ],
                },
            },
            'markets': {
                'BTC/BRL': { 'id': 'BRLBTC', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'suffix': 'Bitcoin' },
                'LTC/BRL': { 'id': 'BRLLTC', 'symbol': 'LTC/BRL', 'base': 'LTC', 'quote': 'BRL', 'suffix': 'Litecoin' },
                'BCH/BRL': { 'id': 'BRLBCH', 'symbol': 'BCH/BRL', 'base': 'BCH', 'quote': 'BRL', 'suffix': 'BCash' },
                'XRP/BRL': { 'id': 'BRLXRP', 'symbol': 'XRP/BRL', 'base': 'XRP', 'quote': 'BRL', 'suffix': 'Ripple' },
                'ETH/BRL': { 'id': 'BRLETH', 'symbol': 'ETH/BRL', 'base': 'ETH', 'quote': 'BRL', 'suffix': 'Ethereum' },
            },
            'tickers': [
                'BTC/BRL',
                'LTC/BRL',
                'BCH/BRL',
                'XRP/BRL',
                'ETH/BRL',
            ],
            'fees': {
                'trading': {
                    'maker': 0.3 / 100,
                    'taker': 0.7 / 100,
                },
            },
        });
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        let market = this.market (symbol);
        let orderbook = await this.publicGetCoinOrderbook (this.extend ({
            'coin': market['base'],
        }, params));
        return this.parseOrderBook (orderbook);
    }

    async fetchTicker (symbol, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetCoinTicker (this.extend ({
            'coin': market['base'],
        }, params));
        let ticker = response['ticker'];
        let timestamp = parseInt (ticker['date']) * 1000;
        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, 'buy'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'sell'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'vol'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

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

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        let market = this.market (symbol);
        let method = 'publicGetCoinTrades';
        let request = {
            'coin': market['base'],
        };
        if (since !== undefined) {
            method += 'From';
            request['from'] = parseInt (since / 1000);
        }
        let to = this.safeInteger (params, 'to');
        if (to !== undefined)
            method += 'To';
        let response = await this[method] (this.extend (request, params));
        return this.parseTrades (response, market, since, limit);
    }

    async fetchBalance (params = {}) {
        let response = await this.privatePostGetAccountInfo ();
        let balances = response['response_data']['balance'];
        let result = { 'info': response };
        let currencies = Object.keys (this.currencies);
        for (let i = 0; i < currencies.length; i++) {
            let currency = currencies[i];
            let lowercase = currency.toLowerCase ();
            let account = this.account ();
            if (lowercase in balances) {
                account['free'] = parseFloat (balances[lowercase]['available']);
                account['total'] = parseFloat (balances[lowercase]['total']);
                account['used'] = account['total'] - account['free'];
            }
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        let order = {
            'coin_pair': this.marketId (symbol),
        };
        let method = 'privatePostPlace' + this.capitalize (side) + 'Order';
        if (type === 'limit') {
            order['limit_price'] = price;
            order['quantity'] = amount;
        } else {
            method = 'privatePostPlaceMarket' + this.capitalize (side) + 'Order';
            if (side === 'buy') {
                order['cost'] = (amount * price).toFixed (5);
            } else {
                order['quantity'] = amount;
            }
        }
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['response_data']['order']['order_id'].toString (),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' cancelOrder () requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.privatePostCancelOrder (this.extend ({
            'coin_pair': market['id'],
            'order_id': id,
        }, params));
        return this.parseOrder (response['response_data']['order'], market);
    }

    parseOrderStatus (status) {
        const statuses = {
            '2': 'open',
            '3': 'canceled',
            '4': 'closed',
        };
        return this.safeString (statuses, status, status);
    }

    parseOrder (order, market = undefined) {
        const id = this.safeString (order, 'order_id');
        let side = undefined;
        if ('order_type' in order)
            side = (order['order_type'] === 1) ? 'buy' : 'sell';
        let status = this.parseOrderStatus (this.safeString (order, 'status'));
        let symbol = undefined;
        if (market === undefined) {
            let marketId = this.safeString (order, 'coin_pair');
            market = this.safeValue (this.markets_by_id, marketId);
        }
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        let timestamp = this.safeInteger (order, 'created_timestamp');
        if (timestamp !== undefined) {
            timestamp = timestamp * 1000;
        }
        let fee = {
            'cost': this.safeFloat (order, 'fee'),
            'currency': market['quote'],
        };
        let price = this.safeFloat (order, 'limit_price');
        // price = this.safeFloat (order, 'executed_price_avg', price);
        let average = this.safeFloat (order, 'executed_price_avg');
        let amount = this.safeFloat (order, 'quantity');
        let filled = this.safeFloat (order, 'executed_quantity');
        let remaining = amount - filled;
        let cost = amount * average;
        let lastTradeTimestamp = this.safeInteger (order, 'updated_timestamp');
        if (lastTradeTimestamp !== undefined) {
            lastTradeTimestamp = lastTradeTimestamp * 1000;
        }
        let result = {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': symbol,
            'type': 'limit',
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
            'trades': undefined, // todo parse trades (operations)
        };
        return result;
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = undefined;
        response = await this.privatePostGetOrder (this.extend ({
            'coin_pair': market['id'],
            'order_id': parseInt (id),
        }, params));
        return this.parseOrder (response['response_data']['order']);
    }

    async withdraw (code, amount, address, tag = undefined, params = {}) {
        this.checkAddress (address);
        await this.loadMarkets ();
        let currency = this.currency (code);
        let request = {
            'coin': currency['id'],
            'quantity': amount.toFixed (10),
            'address': address,
        };
        if (code === 'BRL') {
            let account_ref = ('account_ref' in params);
            if (!account_ref) {
                throw new ExchangeError (this.id + ' requires account_ref parameter to withdraw ' + code);
            }
        } else if (code !== 'LTC') {
            let tx_fee = ('tx_fee' in params);
            if (!tx_fee) {
                throw new ExchangeError (this.id + ' requires tx_fee parameter to withdraw ' + code);
            }
            if (code === 'XRP') {
                if (tag === undefined) {
                    if (!('destination_tag' in params)) {
                        throw new ExchangeError (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code);
                    }
                } else {
                    request['destination_tag'] = tag;
                }
            }
        }
        let response = await this.privatePostWithdrawCoin (this.extend (request, params));
        return {
            'info': response,
            'id': response['response_data']['withdrawal']['id'],
        };
    }

    parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
        return [
            ohlcv['timestamp'] * 1000,
            parseFloat (ohlcv['open']),
            parseFloat (ohlcv['high']),
            parseFloat (ohlcv['low']),
            parseFloat (ohlcv['close']),
            parseFloat (ohlcv['volume']),
        ];
    }

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'precision': this.timeframes[timeframe],
            'coin': market.id.toLowerCase (),
        };
        if (since !== undefined) {
            request['from'] = parseInt (since / 1000);
            if (limit !== undefined) {
                request['to'] = (this.sum (request['from'], limit * this.parseTimeframe (timeframe)));
            } else {
                request['to'] = this.sum (this.seconds (), 1);
            }
        }
        let response = await this.v4PublicGetCoinCandle (this.extend (request, params));
        return this.parseOHLCVs (response.candles, market, timeframe, since, limit);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' fetchOrders () requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = undefined;
        response = await this.privatePostListOrders (this.extend ({
            'coin_pair': market['base'],
        }, params));
        let orders = response['response_data']['orders'];
        for (let i = 0; i < orders.length; i++) {
            orders[i] = this.parseOrder (orders[i]);
        }
        return orders;
    }

    async fetchTickers (symbols = undefined, params = {}) {
        let tickers = {};
        for (let i = 0; i < this.tickers.length; i++) {
            let ticker = this.tickers[i];
            tickers[ticker] = await this.fetchTicker (ticker);
        }
        return tickers;
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/';
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public' || (api === 'v4Public')) {
            url += this.implodeParams (path, params);
            if (Object.keys (query).length)
                url += '?' + this.urlencode (query);
        } else {
            this.checkRequiredCredentials ();
            url += this.version + '/';
            let nonce = this.nonce ();
            body = this.urlencode (this.extend ({
                'tapi_method': path,
                'tapi_nonce': nonce,
            }, params));
            let auth = '/tapi/' + this.version + '/' + '?' + body;
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'TAPI-ID': this.apiKey,
                'TAPI-MAC': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'),
            };
        }
        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 ('error_message' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<commit_msg>mercado fetchTickers removed<commit_after>'use strict';

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

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

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

module.exports = class mercado extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'mercado',
            'name': 'Mercado Bitcoin',
            'countries': ['BR'], // Brazil
            'rateLimit': 1000,
            'version': 'v3',
            'has': {
                'CORS': true,
                'createMarketOrder': true,
                'fetchOrder': true,
                'withdraw': true,
                'fetchOHLCV': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
                'fetchTicker': true,
                'fetchTickers': false,
            },
            'timeframes': {
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '1w': '1w',
                '2w': '2w',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg',
                'api': {
                    'public': 'https://www.mercadobitcoin.net/api',
                    'private': 'https://www.mercadobitcoin.net/tapi',
                    'v4Public': 'https://www.mercadobitcoin.com.br/v4',
                },
                'www': 'https://www.mercadobitcoin.com.br',
                'doc': [
                    'https://www.mercadobitcoin.com.br/api-doc',
                    'https://www.mercadobitcoin.com.br/trade-api',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        '{coin}/orderbook/', // last slash critical
                        '{coin}/ticker/',
                        '{coin}/trades/',
                        '{coin}/trades/{from}/',
                        '{coin}/trades/{from}/{to}',
                        '{coin}/day-summary/{year}/{month}/{day}/',
                    ],
                },
                'private': {
                    'post': [
                        'cancel_order',
                        'get_account_info',
                        'get_order',
                        'get_withdrawal',
                        'list_system_messages',
                        'list_orders',
                        'list_orderbook',
                        'place_buy_order',
                        'place_sell_order',
                        'place_market_buy_order',
                        'place_market_sell_order',
                        'withdraw_coin',
                    ],
                },
                'v4Public': {
                    'get': [
                        '{coin}/candle/',
                    ],
                },
            },
            'markets': {
                'BTC/BRL': { 'id': 'BRLBTC', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'suffix': 'Bitcoin' },
                'LTC/BRL': { 'id': 'BRLLTC', 'symbol': 'LTC/BRL', 'base': 'LTC', 'quote': 'BRL', 'suffix': 'Litecoin' },
                'BCH/BRL': { 'id': 'BRLBCH', 'symbol': 'BCH/BRL', 'base': 'BCH', 'quote': 'BRL', 'suffix': 'BCash' },
                'XRP/BRL': { 'id': 'BRLXRP', 'symbol': 'XRP/BRL', 'base': 'XRP', 'quote': 'BRL', 'suffix': 'Ripple' },
                'ETH/BRL': { 'id': 'BRLETH', 'symbol': 'ETH/BRL', 'base': 'ETH', 'quote': 'BRL', 'suffix': 'Ethereum' },
            },
            'tickers': [
                'BTC/BRL',
                'LTC/BRL',
                'BCH/BRL',
                'XRP/BRL',
                'ETH/BRL',
            ],
            'fees': {
                'trading': {
                    'maker': 0.3 / 100,
                    'taker': 0.7 / 100,
                },
            },
        });
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        let market = this.market (symbol);
        let orderbook = await this.publicGetCoinOrderbook (this.extend ({
            'coin': market['base'],
        }, params));
        return this.parseOrderBook (orderbook);
    }

    async fetchTicker (symbol, params = {}) {
        let market = this.market (symbol);
        let response = await this.publicGetCoinTicker (this.extend ({
            'coin': market['base'],
        }, params));
        let ticker = response['ticker'];
        let timestamp = parseInt (ticker['date']) * 1000;
        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, 'buy'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'sell'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'vol'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

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

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        let market = this.market (symbol);
        let method = 'publicGetCoinTrades';
        let request = {
            'coin': market['base'],
        };
        if (since !== undefined) {
            method += 'From';
            request['from'] = parseInt (since / 1000);
        }
        let to = this.safeInteger (params, 'to');
        if (to !== undefined)
            method += 'To';
        let response = await this[method] (this.extend (request, params));
        return this.parseTrades (response, market, since, limit);
    }

    async fetchBalance (params = {}) {
        let response = await this.privatePostGetAccountInfo ();
        let balances = response['response_data']['balance'];
        let result = { 'info': response };
        let currencies = Object.keys (this.currencies);
        for (let i = 0; i < currencies.length; i++) {
            let currency = currencies[i];
            let lowercase = currency.toLowerCase ();
            let account = this.account ();
            if (lowercase in balances) {
                account['free'] = parseFloat (balances[lowercase]['available']);
                account['total'] = parseFloat (balances[lowercase]['total']);
                account['used'] = account['total'] - account['free'];
            }
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        let order = {
            'coin_pair': this.marketId (symbol),
        };
        let method = 'privatePostPlace' + this.capitalize (side) + 'Order';
        if (type === 'limit') {
            order['limit_price'] = price;
            order['quantity'] = amount;
        } else {
            method = 'privatePostPlaceMarket' + this.capitalize (side) + 'Order';
            if (side === 'buy') {
                order['cost'] = (amount * price).toFixed (5);
            } else {
                order['quantity'] = amount;
            }
        }
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['response_data']['order']['order_id'].toString (),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' cancelOrder () requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.privatePostCancelOrder (this.extend ({
            'coin_pair': market['id'],
            'order_id': id,
        }, params));
        return this.parseOrder (response['response_data']['order'], market);
    }

    parseOrderStatus (status) {
        const statuses = {
            '2': 'open',
            '3': 'canceled',
            '4': 'closed',
        };
        return this.safeString (statuses, status, status);
    }

    parseOrder (order, market = undefined) {
        const id = this.safeString (order, 'order_id');
        let side = undefined;
        if ('order_type' in order)
            side = (order['order_type'] === 1) ? 'buy' : 'sell';
        let status = this.parseOrderStatus (this.safeString (order, 'status'));
        let symbol = undefined;
        if (market === undefined) {
            let marketId = this.safeString (order, 'coin_pair');
            market = this.safeValue (this.markets_by_id, marketId);
        }
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        let timestamp = this.safeInteger (order, 'created_timestamp');
        if (timestamp !== undefined) {
            timestamp = timestamp * 1000;
        }
        let fee = {
            'cost': this.safeFloat (order, 'fee'),
            'currency': market['quote'],
        };
        let price = this.safeFloat (order, 'limit_price');
        // price = this.safeFloat (order, 'executed_price_avg', price);
        let average = this.safeFloat (order, 'executed_price_avg');
        let amount = this.safeFloat (order, 'quantity');
        let filled = this.safeFloat (order, 'executed_quantity');
        let remaining = amount - filled;
        let cost = amount * average;
        let lastTradeTimestamp = this.safeInteger (order, 'updated_timestamp');
        if (lastTradeTimestamp !== undefined) {
            lastTradeTimestamp = lastTradeTimestamp * 1000;
        }
        let result = {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': symbol,
            'type': 'limit',
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
            'trades': undefined, // todo parse trades (operations)
        };
        return result;
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = undefined;
        response = await this.privatePostGetOrder (this.extend ({
            'coin_pair': market['id'],
            'order_id': parseInt (id),
        }, params));
        return this.parseOrder (response['response_data']['order']);
    }

    async withdraw (code, amount, address, tag = undefined, params = {}) {
        this.checkAddress (address);
        await this.loadMarkets ();
        let currency = this.currency (code);
        let request = {
            'coin': currency['id'],
            'quantity': amount.toFixed (10),
            'address': address,
        };
        if (code === 'BRL') {
            let account_ref = ('account_ref' in params);
            if (!account_ref) {
                throw new ExchangeError (this.id + ' requires account_ref parameter to withdraw ' + code);
            }
        } else if (code !== 'LTC') {
            let tx_fee = ('tx_fee' in params);
            if (!tx_fee) {
                throw new ExchangeError (this.id + ' requires tx_fee parameter to withdraw ' + code);
            }
            if (code === 'XRP') {
                if (tag === undefined) {
                    if (!('destination_tag' in params)) {
                        throw new ExchangeError (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code);
                    }
                } else {
                    request['destination_tag'] = tag;
                }
            }
        }
        let response = await this.privatePostWithdrawCoin (this.extend (request, params));
        return {
            'info': response,
            'id': response['response_data']['withdrawal']['id'],
        };
    }

    parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
        return [
            ohlcv['timestamp'] * 1000,
            parseFloat (ohlcv['open']),
            parseFloat (ohlcv['high']),
            parseFloat (ohlcv['low']),
            parseFloat (ohlcv['close']),
            parseFloat (ohlcv['volume']),
        ];
    }

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'precision': this.timeframes[timeframe],
            'coin': market.id.toLowerCase (),
        };
        if (since !== undefined) {
            request['from'] = parseInt (since / 1000);
            if (limit !== undefined) {
                request['to'] = (this.sum (request['from'], limit * this.parseTimeframe (timeframe)));
            } else {
                request['to'] = this.sum (this.seconds (), 1);
            }
        }
        let response = await this.v4PublicGetCoinCandle (this.extend (request, params));
        return this.parseOHLCVs (response.candles, market, timeframe, since, limit);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' fetchOrders () requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = undefined;
        response = await this.privatePostListOrders (this.extend ({
            'coin_pair': market['base'],
        }, params));
        let orders = response['response_data']['orders'];
        for (let i = 0; i < orders.length; i++) {
            orders[i] = this.parseOrder (orders[i]);
        }
        return orders;
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/';
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public' || (api === 'v4Public')) {
            url += this.implodeParams (path, params);
            if (Object.keys (query).length)
                url += '?' + this.urlencode (query);
        } else {
            this.checkRequiredCredentials ();
            url += this.version + '/';
            let nonce = this.nonce ();
            body = this.urlencode (this.extend ({
                'tapi_method': path,
                'tapi_nonce': nonce,
            }, params));
            let auth = '/tapi/' + this.version + '/' + '?' + body;
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'TAPI-ID': this.apiKey,
                'TAPI-MAC': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'),
            };
        }
        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 ('error_message' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<|endoftext|>


<commit_before>function Message(data) {
    this.id = Number(data.postid);
    this.user = {
        id: Number(data.userid)
        , name: data.username
    };
    this.value = data.message;
    this.posted = data.posted;
    this.$el = null; //after createDOM() -> div.message
    this.createDOM();
}

Message.prototype.createDOM = function () {
    var user = this.user
        , el = document.createElement('div');

    el.id = 'min-message' + this.id;
    el.title = 'Wysłano: ' + this.posted;
    el.classList.add('min-chat__content__message');

    if (user.id < 0)
        var $a = el.appendChild(document.createElement('span'));
    else {
        var $a = el.appendChild(document.createElement('a'));
        $a.href = 'http://forum.miroslawzelent.pl/user/' + encodeURIComponent(user.name);
    }
    $a.classList.add('min-chat__content__message__link');
    $a.innerHTML = user.name;

    var $c = el.appendChild(document.createElement('span'));
    $c.innerHTML = this.value;  //value is already escaped from html tags

    this.$el = el;
};

Message.prototype.getDOM = function () {
    //too lame
    return this.$el;
};
<commit_msg>Bold @nicks<commit_after>function Message(data) {
    this.id = Number(data.postid);
    this.user = {
        id: Number(data.userid)
        , name: data.username
    };
    this.value = data.message;
    this.posted = data.posted;
    this.$el = null; //after createDOM() -> div.message
    this.createDOM();
}

Message.prototype.createDOM = function () {
    var user = this.user
        , el = document.createElement('div');

    el.id = 'min-message' + this.id;
    el.title = 'Wysłano: ' + this.posted;
    el.classList.add('min-chat__content__message');

    if (user.id < 0)
        var $a = el.appendChild(document.createElement('span'));
    else {
        var $a = el.appendChild(document.createElement('a'));
        $a.href = 'http://forum.miroslawzelent.pl/user/' + encodeURIComponent(user.name);
    }
    $a.classList.add('min-chat__content__message__link');
    $a.innerHTML = user.name;

    var $c = el.appendChild(document.createElement('span'));
    var content = this.value.split(' ');
    for(var word = 0; word < content.length; word++) {
      if(content[word].startsWith('@') && content[word].length > 3){
        var spaces = content[word].match(/_/g) || [];
        spaces = spaces.length;

        content[word] = `<b>${content[word].split('_').join(' ')}</b>`;
        word += spaces;
      }
    }

    $c.innerHTML = content.join(' ');  //value is already escaped from html tags

    this.$el = el;
};

Message.prototype.getDOM = function () {
    //too lame
    return this.$el;
};
<|endoftext|>


<commit_before>var MiniMap;
require('map', 'tile', 'building', 'unit');

MiniMap = function () {
    var map, container, buildingsLayer, unitsLayer, width, height, mapWidth,
            mapHeight, buildingsLayerCtx, unitsLayerCtx, xRatio, yRatio,
            tileWidth, tileHeight, changedTerrain, changedBuildings,
            changedUnits, drawTileLine, fill, buildingsLayerIndex, createIndex,
            xRatioC, yRatioC, initRenderer, renderUnitsLayer,
            renderBuildingsLayer, renderTerrainLayer;

    map = [];
    buildingsLayer = document.createElement('canvas');
    buildingsLayerCtx = buildingsLayer.getContext('2d');
    unitsLayer = document.createElement('canvas');
    unitsLayerCtx = unitsLayer.getContext('2d');
    changedTerrain = false;
    changedBuildings = false;
    changedUnits = false;

    this.setMap = function (newMap) {
        if (!(newMap instanceof Array) || !newMap[0] ||
                !(newMap[0] instanceof Array)) {
            throw new Error("invalid format of map");
        }
        map = newMap;
        mapHeight = map.length;
        mapWidth = map[0].length;
        if (container) {
            initRenderer();
        }
    };

    this.setContainer = function (newContainer) {
        if (!(newContainer instanceof HTMLElement)) {
            throw new Error("container must be an HTML element");
        }
        container = newContainer;
        width = container.offsetWidth;
        height = container.offsetHeight;
        buildingsLayer.width = unitsLayer.width = width;
        buildingsLayer.height = unitsLayer.height = height;
        newContainer = document.createElement('div');
        newContainer.style.height = '0px';
        newContainer.appendChild(buildingsLayer);
        container.appendChild(newContainer);
        container.appendChild(unitsLayer);
        if (mapWidth) {
            initRenderer();
        }
    };

    this.onBuildingChange = function (building) {
        changedBuildings.push(building);
    };

    this.onUnitChange = function (unit) {
        changedUnits.push(unit);
    };

    this.render = function () {
        changedTerrain && renderTerrainLayer();
        changedBuildings.length && renderBuildingsLayer();
        changedUnits.length && renderUnitsLayer();
    };

    renderUnitsLayer = function () {
        var i, unit;
        for (i = changedUnits.length; i--;) {
            unit = changedUnits[i];
            unitsLayerCtx.fillStyle = unit.color;
            switch (unit.action) {
                case 2:
                    unitsLayerCtx.globalCompositeOperation = 'destination-out';
                    unitsLayerCtx.fillRect(unit.lastX * xRatio,
                            unit.lastY * yRatio, xRatioC, yRatioC);
                case 0:
                    unitsLayerCtx.globalCompositeOperation = 'source-over';
                    unitsLayerCtx.fillRect(unit.x * xRatio, unit.y * yRatio,
                            xRatioC, yRatioC);
                    break;
                case 1:
                    unitsLayerCtx.globalCompositeOperation = 'destination-out';
                    unitsLayerCtx.fillRect(unit.x * xRatio, unit.y * yRatio,
                            xRatioC, yRatioC);
                    break;
            }
        }
        changedUnits = [];
    };

    renderBuildingsLayer = function () {
        var i, building, x1, y1, x2, y2, x3, y3, x4, y4;
        for (i = changedBuildings.length; i--;) {
            building = changedBuildings[i];
            buildingsLayerCtx.globalCompositeOperation = building.type ===
                    false ? 'destination-out' : 'source-over';
            x1 = building.x;
            y1 = building.y;
            x2 = x1 + building.width;
            y2 = y1 + Math.floor(building.width / 2);
            x3 = x1 - Math.floor(building.height / 2);
            y3 = y1 + building.height;
            x4 = x3 + building.width;
            y4 = y3 + Math.floor(building.width / 2);
            buildingsLayerCtx.fillStyle = building.color;
            drawTileLine(x1, y1, x2, y2, buildingsLayerCtx);
            drawTileLine(x1, y1, x3, y3, buildingsLayerCtx);
            drawTileLine(x2, y2, x4, y4, buildingsLayerCtx);
            drawTileLine(x3, y3, x4, y4, buildingsLayerCtx);
            fill(Math.round((x1 + x4) / 2), Math.round((y1 + y4) / 2),
                    buildingsLayerCtx);
        }
        changedBuildings = [];
    };

    renderTerrainLayer = function () {
        var i, j, row;
        for (i = map.length; i--;) {
            row = map[i];
            for (j = row.length; j--;) {
                buildingsLayerCtx.fillStyle = row[j].minimap;
                buildingsLayerCtx.fillRect(xRatio * j, yRatio * i,
                        tileWidth, tileHeight);
            }
        }
        container.style.backgroundImage = 'url(' + buildingsLayer.toDataURL() +
                ')';
        buildingsLayerCtx.clearRect(0, 0, width, height);
        changedTerrain = false;
    };

    createIndex = function () {
        var index, i, j;
        index = new Array(mapWidth);
        for (i = mapWidth; i--;) {
            index[i] = new Array(mapHeight);
            for (j = mapHeight; j--;) {
                index[i][j] = false;
            }
        }
        return index;
    };

    fill = function (x, y, ctx, fillColor) {
        var thisColor;
        if ((x >= mapWidth) || (x < 0) || (y >= mapHeight) || (y < 0)) {
            return;
        }
        thisColor = buildingsLayerIndex[x][y];
        if (fillColor === undefined) {
            fillColor = thisColor;
        }
        if (thisColor == fillColor) {
            ctx.fillRect(x * xRatio, y * yRatio, xRatioC, yRatioC);
            buildingsLayerIndex[x][y] = !buildingsLayerIndex[x][y];
            fill(x - 1, y, ctx, fillColor);
            fill(x + 1, y, ctx, fillColor);
            fill(x, y - 1, ctx, fillColor);
            fill(x, y + 1, ctx, fillColor);
        }
    };

    drawTileLine = function (x1, y1, x2, y2, ctx) {
        var len, vx, vy, rx, ry;
        vx = x2 - x1;
        vy = y2 - y1;
        len = Math.sqrt(vx * vx + vy * vy);
        vx /= len;
        vy /= len;
        ctx.fillRect(xRatio * Math.round(x1), yRatio * Math.round(y1),
                xRatioC, yRatioC);
        do {
            x1 += vx;
            y1 += vy;
            rx = Math.round(x1);
            ry = Math.round(y1);
            if (buildingsLayerIndex[rx] &&
                    (buildingsLayerIndex[rx][ry] !== undefined)) {
                buildingsLayerIndex[rx][ry] = true;
                ctx.fillRect(xRatio * rx, yRatio * ry, xRatioC, yRatioC);
            }
        } while ((Math.round(x1) != x2) && (Math.round(y1) != y2))
    };

    initRenderer = function () {
        xRatio = width / mapWidth;
        yRatio = height / mapHeight;
        xRatioC = Math.ceil(xRatio);
        yRatioC = Math.ceil(yRatio);
        tileWidth = Math.ceil(xRatio);
        tileHeight = Math.ceil(yRatio);
        changedTerrain = true;
        changedBuildings = [];
        changedUnits = [];
        buildingsLayerIndex = createIndex();
    };
};<commit_msg>minimap fix for IE9 RC<commit_after>var MiniMap;
require('map', 'tile', 'building', 'unit');

MiniMap = function () {
    var map, container, buildingsLayer, unitsLayer, width, height, mapWidth,
            mapHeight, buildingsLayerCtx, unitsLayerCtx, xRatio, yRatio,
            tileWidth, tileHeight, changedTerrain, changedBuildings,
            changedUnits, drawTileLine, fill, buildingsLayerIndex, createIndex,
            xRatioC, yRatioC, initRenderer, renderUnitsLayer,
            renderBuildingsLayer, renderTerrainLayer, terrainLayer,
            terrainLayerCtx;

    map = [];
    terrainLayer = document.createElement('canvas');
    terrainLayerCtx = terrainLayer.getContext('2d');
    buildingsLayer = document.createElement('canvas');
    buildingsLayerCtx = buildingsLayer.getContext('2d');
    unitsLayer = document.createElement('canvas');
    unitsLayerCtx = unitsLayer.getContext('2d');
    changedTerrain = false;
    changedBuildings = false;
    changedUnits = false;

    this.setMap = function (newMap) {
        if (!(newMap instanceof Array) || !newMap[0] ||
                !(newMap[0] instanceof Array)) {
            throw new Error("invalid format of map");
        }
        map = newMap;
        mapHeight = map.length;
        mapWidth = map[0].length;
        if (container) {
            initRenderer();
        }
    };

    this.setContainer = function (newContainer) {
        if (!(newContainer instanceof HTMLElement)) {
            throw new Error("container must be an HTML element");
        }
        container = newContainer;
        width = container.offsetWidth;
        height = container.offsetHeight;
        terrainLayer.width = buildingsLayer.width = unitsLayer.width = width;
        terrainLayer.height = buildingsLayer.height = unitsLayer.height =
                height;
        newContainer = document.createElement('div');
        newContainer.style.height = '0px';
        newContainer.appendChild(terrainLayer);
        container.appendChild(newContainer);
        newContainer = document.createElement('div');
        newContainer.style.height = '0px';
        newContainer.appendChild(buildingsLayer);
        container.appendChild(newContainer);
        container.appendChild(unitsLayer);
        if (mapWidth) {
            initRenderer();
        }
    };

    this.onBuildingChange = function (building) {
        changedBuildings.push(building);
    };

    this.onUnitChange = function (unit) {
        changedUnits.push(unit);
    };

    this.render = function () {
        changedTerrain && renderTerrainLayer();
        changedBuildings.length && renderBuildingsLayer();
        changedUnits.length && renderUnitsLayer();
    };

    renderUnitsLayer = function () {
        var i, unit;
        for (i = changedUnits.length; i--;) {
            unit = changedUnits[i];
            unitsLayerCtx.fillStyle = unit.color;
            switch (unit.action) {
                case 2:
                    unitsLayerCtx.globalCompositeOperation = 'destination-out';
                    unitsLayerCtx.fillRect(unit.lastX * xRatio,
                            unit.lastY * yRatio, xRatioC, yRatioC);
                case 0:
                    unitsLayerCtx.globalCompositeOperation = 'source-over';
                    unitsLayerCtx.fillRect(unit.x * xRatio, unit.y * yRatio,
                            xRatioC, yRatioC);
                    break;
                case 1:
                    unitsLayerCtx.globalCompositeOperation = 'destination-out';
                    unitsLayerCtx.fillRect(unit.x * xRatio, unit.y * yRatio,
                            xRatioC, yRatioC);
                    break;
            }
        }
        changedUnits = [];
    };

    renderBuildingsLayer = function () {
        var i, building, x1, y1, x2, y2, x3, y3, x4, y4;
        for (i = changedBuildings.length; i--;) {
            building = changedBuildings[i];
            buildingsLayerCtx.globalCompositeOperation = building.type ===
                    false ? 'destination-out' : 'source-over';
            x1 = building.x;
            y1 = building.y;
            x2 = x1 + building.width;
            y2 = y1 + Math.floor(building.width / 2);
            x3 = x1 - Math.floor(building.height / 2);
            y3 = y1 + building.height;
            x4 = x3 + building.width;
            y4 = y3 + Math.floor(building.width / 2);
            buildingsLayerCtx.fillStyle = building.color;
            drawTileLine(x1, y1, x2, y2, buildingsLayerCtx);
            drawTileLine(x1, y1, x3, y3, buildingsLayerCtx);
            drawTileLine(x2, y2, x4, y4, buildingsLayerCtx);
            drawTileLine(x3, y3, x4, y4, buildingsLayerCtx);
            fill(Math.round((x1 + x4) / 2), Math.round((y1 + y4) / 2),
                    buildingsLayerCtx);
        }
        changedBuildings = [];
    };

    renderTerrainLayer = function () {
        var i, j, row;
        for (i = map.length; i--;) {
            row = map[i];
            for (j = row.length; j--;) {
                terrainLayerCtx.fillStyle = row[j].minimap;
                terrainLayerCtx.fillRect(xRatio * j, yRatio * i,
                        tileWidth, tileHeight);
            }
        }
        changedTerrain = false;
    };

    createIndex = function () {
        var index, i, j;
        index = new Array(mapWidth);
        for (i = mapWidth; i--;) {
            index[i] = new Array(mapHeight);
            for (j = mapHeight; j--;) {
                index[i][j] = false;
            }
        }
        return index;
    };

    fill = function (x, y, ctx, fillColor) {
        var thisColor;
        if ((x >= mapWidth) || (x < 0) || (y >= mapHeight) || (y < 0)) {
            return;
        }
        thisColor = buildingsLayerIndex[x][y];
        if (fillColor === undefined) {
            fillColor = thisColor;
        }
        if (thisColor == fillColor) {
            ctx.fillRect(x * xRatio, y * yRatio, xRatioC, yRatioC);
            buildingsLayerIndex[x][y] = !buildingsLayerIndex[x][y];
            fill(x - 1, y, ctx, fillColor);
            fill(x + 1, y, ctx, fillColor);
            fill(x, y - 1, ctx, fillColor);
            fill(x, y + 1, ctx, fillColor);
        }
    };

    drawTileLine = function (x1, y1, x2, y2, ctx) {
        var len, vx, vy, rx, ry;
        vx = x2 - x1;
        vy = y2 - y1;
        len = Math.sqrt(vx * vx + vy * vy);
        vx /= len;
        vy /= len;
        ctx.fillRect(xRatio * Math.round(x1), yRatio * Math.round(y1),
                xRatioC, yRatioC);
        do {
            x1 += vx;
            y1 += vy;
            rx = Math.round(x1);
            ry = Math.round(y1);
            if (buildingsLayerIndex[rx] &&
                    (buildingsLayerIndex[rx][ry] !== undefined)) {
                buildingsLayerIndex[rx][ry] = true;
                ctx.fillRect(xRatio * rx, yRatio * ry, xRatioC, yRatioC);
            }
        } while ((Math.round(x1) != x2) && (Math.round(y1) != y2))
    };

    initRenderer = function () {
        xRatio = width / mapWidth;
        yRatio = height / mapHeight;
        xRatioC = Math.ceil(xRatio);
        yRatioC = Math.ceil(yRatio);
        tileWidth = Math.ceil(xRatio);
        tileHeight = Math.ceil(yRatio);
        changedTerrain = true;
        changedBuildings = [];
        changedUnits = [];
        buildingsLayerIndex = createIndex();
    };
};<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';

const tutorialHighlightStyle = 'tutorial';
const pulseStyle = 'mini-pulse';

class TutorialView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isShowing: false
    };
    const { onTutorialNext, onTutorialPrevious, onTutorialMore, onTutorialClosed } = this.props;
    this.handleNextTutorial =  onTutorialNext;
    this.handlePreviousTutorial = onTutorialPrevious;
    this.handleShowMore = onTutorialMore;
    this.handleCloseTutorial = onTutorialClosed;
  }

  componentDidMount() {
    const { visible, steps } = this.props;
    let self = this;
    if (visible && steps && steps.length > 0) {
      // the first time we mount on a page, wait a little before showing first tutorial
      setTimeout(() => {
        self.setState({ isShowing: true });
      }, 1500);
    }
  }

  componentWillUpdate(nextProps, nextState) {
    if (this.props.currentStep !== nextProps.currentStep ||
        this.state.isShowing !== nextState.isShowing) {
      let tutorialStep = this.props.steps[nextProps.currentStep];
      if (tutorialStep) {
        this.showTutorialHighlights(tutorialStep);
      }
    }
    if (this.props.visible !== nextProps.visible && !nextProps.visible) {
      this.removeAllTutorialHighlights();
    }
  }

  showTutorialHighlights(tutorialStep) {
    this.addClass(tutorialStep.element, tutorialHighlightStyle);
    if (tutorialStep.pulseElement) {
      this.addClass(tutorialStep.pulseElement, pulseStyle);
    } else {
      this.addClass(tutorialStep.element, pulseStyle);
    }
  }

  removeAllTutorialHighlights() {
    this.removeClassFromAllElements(tutorialHighlightStyle);
    this.removeClassFromAllElements(pulseStyle);
  }

  removeClassFromAllElements(className) {
    let elements = document.getElementsByClassName(className);
    for (let i = elements.length - 1; i > -1; i--){
      elements[i].classList.remove(className);
    }
  }

  addClass(elClassName, newClassName) {
    this.removeClassFromAllElements(newClassName);
    setTimeout(() => {
      let elements = document.getElementsByClassName(elClassName);
      for (let i = 0; i < elements.length; i++){
        elements[i].classList.add(newClassName);
      }
    }, 200);
  }

  render() {
    const { steps, visible, currentStep, moreVisible } = this.props;
    const { isShowing } = this.state;
    const tutorialStep = steps[currentStep];

    if (!visible || !isShowing || !tutorialStep) {
      return null;
    }

    const dialogClass = "tutorial-content " + (tutorialStep.location ? tutorialStep.location : "");

    // Only show the More link if there is text defined in the authoring, then toggle visibility if More text is present.
    const more = tutorialStep.more ?
        (moreVisible ?
{tutorialStep.more}
 :

(Click to show more)
)
      : null;

    return (

{tutorialStep.text}

          {tutorialStep.longText &&
{tutorialStep.longText}
}
          {more}

            {currentStep > 0 &&

Back

            }
            {currentStep < steps.length - 1 &&

Next

            }

X

    );
  }
}

TutorialView.propTypes = {
  visible: PropTypes.bool,
  steps: PropTypes.array,
  currentStep: PropTypes.number,
  moreVisible: PropTypes.bool,
  onTutorialMore: PropTypes.func,
  onTutorialNext: PropTypes.func,
  onTutorialPrevious: PropTypes.func,
  onTutorialClosed: PropTypes.func
};

export default TutorialView;
<commit_msg>Re-enable highlight and bounce on tutorial elements when the tutorial is re-shown [#158579950]<commit_after>import React, {PropTypes} from 'react';

const tutorialHighlightStyle = 'tutorial';
const pulseStyle = 'mini-pulse';

class TutorialView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isShowing: false
    };
    const { onTutorialNext, onTutorialPrevious, onTutorialMore, onTutorialClosed } = this.props;
    this.handleNextTutorial =  onTutorialNext;
    this.handlePreviousTutorial = onTutorialPrevious;
    this.handleShowMore = onTutorialMore;
    this.handleCloseTutorial = onTutorialClosed;
  }

  componentDidMount() {
    const { visible, steps } = this.props;
    let self = this;
    if (visible && steps && steps.length > 0) {
      // the first time we mount on a page, wait a little before showing first tutorial
      setTimeout(() => {
        self.setState({ isShowing: true });
      }, 1500);
    }
  }

  componentWillUpdate(nextProps, nextState) {
    if (this.props.currentStep !== nextProps.currentStep ||
      this.state.isShowing !== nextState.isShowing ||
      (this.props.visible !== nextProps.visible && nextProps.visible)) {
      let tutorialStep = this.props.steps[nextProps.currentStep];
      if (tutorialStep) {
        this.showTutorialHighlights(tutorialStep);
      }
    }
    if (this.props.visible !== nextProps.visible && !nextProps.visible) {
      this.removeAllTutorialHighlights();
    }
  }

  showTutorialHighlights(tutorialStep) {
    this.addClass(tutorialStep.element, tutorialHighlightStyle);
    if (tutorialStep.pulseElement) {
      this.addClass(tutorialStep.pulseElement, pulseStyle);
    } else {
      this.addClass(tutorialStep.element, pulseStyle);
    }
  }

  removeAllTutorialHighlights() {
    this.removeClassFromAllElements(tutorialHighlightStyle);
    this.removeClassFromAllElements(pulseStyle);
  }

  removeClassFromAllElements(className) {
    let elements = document.getElementsByClassName(className);
    for (let i = elements.length - 1; i > -1; i--){
      elements[i].classList.remove(className);
    }
  }

  addClass(elClassName, newClassName) {
    this.removeClassFromAllElements(newClassName);
    setTimeout(() => {
      let elements = document.getElementsByClassName(elClassName);
      for (let i = 0; i < elements.length; i++){
        elements[i].classList.add(newClassName);
      }
    }, 200);
  }

  render() {
    const { steps, visible, currentStep, moreVisible } = this.props;
    const { isShowing } = this.state;
    const tutorialStep = steps[currentStep];

    if (!visible || !isShowing || !tutorialStep) {
      return null;
    }

    const dialogClass = "tutorial-content " + (tutorialStep.location ? tutorialStep.location : "");

    // Only show the More link if there is text defined in the authoring, then toggle visibility if More text is present.
    const more = tutorialStep.more ?
        (moreVisible ?
{tutorialStep.more}
 :

(Click to show more)
)
      : null;

    return (

{tutorialStep.text}

          {tutorialStep.longText &&
{tutorialStep.longText}
}
          {more}

            {currentStep > 0 &&

Back

            }
            {currentStep < steps.length - 1 &&

Next

            }

X

    );
  }
}

TutorialView.propTypes = {
  visible: PropTypes.bool,
  steps: PropTypes.array,
  currentStep: PropTypes.number,
  moreVisible: PropTypes.bool,
  onTutorialMore: PropTypes.func,
  onTutorialNext: PropTypes.func,
  onTutorialPrevious: PropTypes.func,
  onTutorialClosed: PropTypes.func
};

export default TutorialView;
<|endoftext|>


<commit_before>/* jshint browser: true */
(function() {
  "use strict";
  /**
   * Confirm this is a nav menu item.
   *
   * @returns {Boolean} True if a nav item.
   */
  function isNavItem(id) {
    return /^menu-(?:\w+?)$/.test(id);
  }

  /**
   * Hide the current menu.
   */
  function hideNavMenu() {
    var menuVisible = document.querySelector(".menu-context.visible");
    var menuActive = document.querySelector(".menu .active");

    // Hide the currently visible menu
    if (menuVisible) {
      menuVisible.classList.remove("visible");
    }

    // Un-highlight the current nav item
    if (menuActive) {
      menuActive.classList.remove("active");
    }
  }

  var menusAreVisible = false,
      Qbody = document.querySelector("body"),
      Qmenu = document.querySelector(".menu");

  Qmenu.addEventListener("mouseover", function(e) {
    // Make sure we are on a nav item
    if (isNavItem(e.target.id)) {
      hideNavMenu();

      // Only display the menus if we are allowed to
      if (menusAreVisible) {
        // Display the currently hovered-over menu
        var contextMenu = e.target.id.match(/^menu-(\w+?)$/)[1];
        document.querySelector(".menu-context." +  contextMenu).classList.add("visible");
        e.target.classList.add("active");
      }
    }
  });

  Qmenu.addEventListener("click", function(e) {
    // Close the menu if the label is clicked again
    if (isNavItem(e.target.id) && menusAreVisible) {
      menusAreVisible = false;
      hideNavMenu();
      return;
    }

    // Make sure the desired menu is not already visible
    if (isNavItem(e.target.id) && !e.target.classList.contains("visible")) {
      // Menus are allowed to be shown
      menusAreVisible = true;

      // Display the desired menu
      var contextMenu = e.target.id.match(/^menu-(\w+?)$/)[1];
      document.querySelector(".menu-context." +  contextMenu).classList.add("visible");
      e.target.classList.add("active");
    }
   });

  Qbody.addEventListener("click", function(e) {
    if (!isNavItem(e.target.id) && !e.target.classList.contains("menu-disabled") && !e.target.classList.contains("blank")) {
      // Menus are not allowed to be shown at this time
      if (menusAreVisible) {
        menusAreVisible = false;
        hideNavMenu();
      }
    }
  });

  var Notepad = (function() {
    var self = null;

    /**
     * Get the class of the current theme.
     *
     * @private
     * @returns {String}
     */
    function __getCurrentTheme(body) {
      return body.className.match(/^win\d{1,2}$/)[0];
    }

    /**
     * Create a Notepad API instance.
     *
     * @constructs Notepad
     * @param {Object} selectors [[Description]]
     */
    function Notepad(selectors) {
      this.fileName = "Note.txt";
      this.selectors = selectors;
      self = this;
    }

    /**
     * Create a new file.
     */
    Notepad.prototype.fileNew = function() {
      self.selectors.textarea.value = "";
      self.selectors.textarea.focus();
    };

    /**
     * Save the document to the computer.
     */
    Notepad.prototype.fileSave = function() {
      // Create a blob object of the contents
      var blob = new Blob([self.selectors.textarea.value], {type: "text/plain"});

      // Internet Explorer/MS Edge
      if (window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveOrOpenBlob(blob, self.fileName);

      // All other browsers
      } else {
        // Create the download link
        var saveLink = document.createElement("a");
        saveLink.style.display = "none";
        saveLink.setAttribute("href", URL.createObjectURL(blob));
        saveLink.setAttribute("download", self.fileName);
        self.selectors.body.appendChild(saveLink);

        // Start the download and remove the link
        saveLink.click();
        self.selectors.body.removeChild(saveLink);
      }
    };

    /**
     * Save a document to the computer using a custom file name.
     */
    Notepad.prototype.fileSaveAs = function() {
      var newName = prompt("Enter the desired file name").trim();

      // Do not permit a blank name
      if (!/^\s*$/.test(newName)) {
        self.fileName = newName + ".txt";
      }
      self.fileSave();
    };

    /**
     * Print the current document.
     */
    Notepad.prototype.filePrint = function() {
      window.print();
    };

    Notepad.prototype.editTimeDate = function() {
      var date = new Date(),
          curHour = date.getHours(),
          curMin  = date.getMinutes(),
          timeOfDay = curHour > 11 ? "PM" : "AM",
          cursorPos = self.selectors.textarea.selectionStart;

      // Midnight
      if (curHour === 0) {
        curHour = "12";

        // Afternoon
      } else if (curHour > 11) {
        curHour -= 12;
      }

      // Pretty print the minutes
      if (curMin < 10) {
        curMin = "0" + curMin;
      }

      // Construct the formatted string
      var dateString = curHour + ":" + curMin + " " + timeOfDay + " " +
                       date.toLocaleDateString();

      // Update the document with the date string
      var front = self.selectors.textarea.value.substring(0, cursorPos),
          back  = self.selectors.textarea.value.substring(cursorPos, self.selectors.textarea.length);

      // Insert the date string into the document
      self.selectors.textarea.value = front + dateString + back;
      self.selectors.textarea.selectionStart = cursorPos;
      self.selectors.textarea.selectionEnd = cursorPos;
      self.selectors.textarea.focus();
    };

    /**
     * Toggle word wrap.
     */
    Notepad.prototype.toggleWordWrap = function() {
      self.selectors.textarea.classList.toggle("no-word-wrap");
    };

    /**
     * Change the UI theme.
     *
     * @param {String} newTheme The desired theme to use.
     * @returns {Boolean} True if the theme could be changed, false otherwise.
     */
    Notepad.prototype.changeTheme = function(newTheme) {
      var validThemes  = ["win7", "win10"],
          currentTheme = __getCurrentTheme(self.selectors.body);

      // The desired theme is already applied or not available
      if (newTheme === currentTheme || validThemes.indexOf(newTheme) === -1) {
        return false;
      }

      // Apply the desired theme
      self.selectors.body.classList.remove(currentTheme);
      self.selectors.body.classList.add(newTheme);
      return true;
    };

    return Notepad;
  })();

  // Create a new Notepad API instance
  var notepad = new Notepad({
    body: document.querySelector("body"),
    textarea: document.querySelector("textarea")
  });

  // File > New command
  document.querySelector(".menu-context #action-new").addEventListener("click", notepad.fileNew);

  // File > Save command
  document.querySelector(".menu-context #action-save").addEventListener("click", notepad.fileSave);

  // File > Save As command
  document.querySelector(".menu-context #action-save-as").addEventListener("click", notepad.fileSaveAs);

  // File > Print command
  document.querySelector(".menu-context #action-print").addEventListener("click", notepad.filePrint);

  // Edit > Time/Date
  document.querySelector(".menu-context #action-time-date").addEventListener("click", notepad.editTimeDate);

  // Format > Word Wrap
  var QwordWrap  = document.querySelector("input#word-wrap");

  // Word wrap is disabled by default
  QwordWrap.checked = false;
  QwordWrap.addEventListener("click", notepad.toggleWordWrap);

  // View > Windows X
  var themeWin7  = document.querySelector(".menu-context input#theme-win7"),
      themeWin10 = document.querySelector(".menu-context input#theme-win10");

  // Default to the Windows 10 theme
  themeWin10.checked = true;
  themeWin7.onchange = themeWin10.onchange = function(e) {
    notepad.changeTheme(e.target.id.match(/^theme-(win\d{1,2})/)[1]);
  };
}());
<commit_msg>Add missing JSDoc<commit_after>/* jshint browser: true */
(function() {
  "use strict";
  /**
   * Confirm this is a nav menu item.
   *
   * @returns {Boolean} True if a nav item.
   */
  function isNavItem(id) {
    return /^menu-(?:\w+?)$/.test(id);
  }

  /**
   * Hide the current menu.
   */
  function hideNavMenu() {
    var menuVisible = document.querySelector(".menu-context.visible");
    var menuActive = document.querySelector(".menu .active");

    // Hide the currently visible menu
    if (menuVisible) {
      menuVisible.classList.remove("visible");
    }

    // Un-highlight the current nav item
    if (menuActive) {
      menuActive.classList.remove("active");
    }
  }

  var menusAreVisible = false,
      Qbody = document.querySelector("body"),
      Qmenu = document.querySelector(".menu");

  Qmenu.addEventListener("mouseover", function(e) {
    // Make sure we are on a nav item
    if (isNavItem(e.target.id)) {
      hideNavMenu();

      // Only display the menus if we are allowed to
      if (menusAreVisible) {
        // Display the currently hovered-over menu
        var contextMenu = e.target.id.match(/^menu-(\w+?)$/)[1];
        document.querySelector(".menu-context." +  contextMenu).classList.add("visible");
        e.target.classList.add("active");
      }
    }
  });

  Qmenu.addEventListener("click", function(e) {
    // Close the menu if the label is clicked again
    if (isNavItem(e.target.id) && menusAreVisible) {
      menusAreVisible = false;
      hideNavMenu();
      return;
    }

    // Make sure the desired menu is not already visible
    if (isNavItem(e.target.id) && !e.target.classList.contains("visible")) {
      // Menus are allowed to be shown
      menusAreVisible = true;

      // Display the desired menu
      var contextMenu = e.target.id.match(/^menu-(\w+?)$/)[1];
      document.querySelector(".menu-context." +  contextMenu).classList.add("visible");
      e.target.classList.add("active");
    }
   });

  Qbody.addEventListener("click", function(e) {
    if (!isNavItem(e.target.id) && !e.target.classList.contains("menu-disabled") &&
        !e.target.classList.contains("blank")) {
      // Menus are not allowed to be shown at this time
      if (menusAreVisible) {
        menusAreVisible = false;
        hideNavMenu();
      }
    }
  });

  var Notepad = (function() {
    var self = null;

    /**
     * Get the class of the current theme.
     *
     * @private
     * @returns {String}
     */
    function __getCurrentTheme(body) {
      return body.className.match(/^win\d{1,2}$/)[0];
    }

    /**
     * Create a Notepad API instance.
     *
     * @constructs Notepad
     * @param {Object} selectors - [[Description]]
     */
    function Notepad(selectors) {
      this.fileName = "Note.txt";
      this.selectors = selectors;
      self = this;
    }

    /**
     * Create a new file.
     */
    Notepad.prototype.fileNew = function() {
      self.selectors.textarea.value = "";
      self.selectors.textarea.focus();
    };

    /**
     * Save the document to the computer.
     */
    Notepad.prototype.fileSave = function() {
      // Create a blob object of the contents
      var blob = new Blob([self.selectors.textarea.value], {type: "text/plain"});

      // Internet Explorer/MS Edge
      if (window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveOrOpenBlob(blob, self.fileName);

      // All other browsers
      } else {
        // Create the download link
        var saveLink = document.createElement("a");
        saveLink.style.display = "none";
        saveLink.setAttribute("href", URL.createObjectURL(blob));
        saveLink.setAttribute("download", self.fileName);
        self.selectors.body.appendChild(saveLink);

        // Start the download and remove the link
        saveLink.click();
        self.selectors.body.removeChild(saveLink);
      }
    };

    /**
     * Save a document to the computer using a custom file name.
     */
    Notepad.prototype.fileSaveAs = function() {
      var newName = prompt("Enter the desired file name").trim();

      // Do not permit a blank name
      if (!/^\s*$/.test(newName)) {
        self.fileName = newName + ".txt";
      }
      self.fileSave();
    };

    /**
     * Print the current document.
     */
    Notepad.prototype.filePrint = function() {
      window.print();
    };

    /**
     * Insert the current time and date into the document
     * at the current cursor position.
     */
    Notepad.prototype.editTimeDate = function() {
      var date = new Date(),
          curHour = date.getHours(),
          curMin  = date.getMinutes(),
          timeOfDay = curHour > 11 ? "PM" : "AM",
          cursorPos = self.selectors.textarea.selectionStart;

      // Midnight
      if (curHour === 0) {
        curHour = "12";

        // Afternoon
      } else if (curHour > 11) {
        curHour -= 12;
      }

      // Pretty print the minutes
      if (curMin < 10) {
        curMin = "0" + curMin;
      }

      // Construct the formatted string
      var dateString = curHour + ":" + curMin + " " + timeOfDay + " " +
                       date.toLocaleDateString();

      // Update the document with the date string
      var front = self.selectors.textarea.value.substring(0, cursorPos),
          back  = self.selectors.textarea.value.substring(cursorPos, self.selectors.textarea.length);

      // Insert the date string into the document
      self.selectors.textarea.value = front + dateString + back;
      self.selectors.textarea.selectionStart = cursorPos;
      self.selectors.textarea.selectionEnd = cursorPos;
      self.selectors.textarea.focus();
    };

    /**
     * Toggle word wrap.
     */
    Notepad.prototype.toggleWordWrap = function() {
      self.selectors.textarea.classList.toggle("no-word-wrap");
    };

    /**
     * Change the UI theme.
     *
     * @param {String} newTheme - The desired theme to use.
     * @returns {Boolean} True if the theme could be changed, false otherwise.
     */
    Notepad.prototype.changeTheme = function(newTheme) {
      var validThemes  = ["win7", "win10"],
          currentTheme = __getCurrentTheme(self.selectors.body);

      // The desired theme is already applied or not available
      if (newTheme === currentTheme || validThemes.indexOf(newTheme) === -1) {
        return false;
      }

      // Apply the desired theme
      self.selectors.body.classList.remove(currentTheme);
      self.selectors.body.classList.add(newTheme);
      return true;
    };

    return Notepad;
  })();

  // Create a new Notepad API instance
  var notepad = new Notepad({
    body: document.querySelector("body"),
    textarea: document.querySelector("textarea")
  });

  // File > New command
  document.querySelector(".menu-context #action-new").addEventListener("click", notepad.fileNew);

  // File > Save command
  document.querySelector(".menu-context #action-save").addEventListener("click", notepad.fileSave);

  // File > Save As command
  document.querySelector(".menu-context #action-save-as").addEventListener("click", notepad.fileSaveAs);

  // File > Print command
  document.querySelector(".menu-context #action-print").addEventListener("click", notepad.filePrint);

  // Edit > Time/Date
  document.querySelector(".menu-context #action-time-date").addEventListener("click", notepad.editTimeDate);

  // Format > Word Wrap
  var QwordWrap = document.querySelector("input#word-wrap");

  // Word wrap is disabled by default
  QwordWrap.checked = false;
  QwordWrap.addEventListener("click", notepad.toggleWordWrap);

  // View > Windows X
  var themeWin7  = document.querySelector(".menu-context input#theme-win7"),
      themeWin10 = document.querySelector(".menu-context input#theme-win10");

  // Default to the Windows 10 theme
  themeWin10.checked = true;
  themeWin7.onchange = themeWin10.onchange = function(e) {
    notepad.changeTheme(e.target.id.match(/^theme-(win\d{1,2})/)[1]);
  };
}());
<|endoftext|>


<commit_before>// Saves options to localStorage.
function save_options() {
  var waybackWarcSource = document.getElementById('waybackWarcSource').value;
  var handling;
  if(document.getElementById('output_save').checked == "checked"){handling = "save";}
  else if(document.getElementById('output_display').checked == "checked"){handling = "display";}
  localStorage["warcSRC"] = waybackWarcSource;
  localStorage["handlingMethod"] = handling;
  // Update status to let user know options were saved.
  var status = $("#status");
  alert("status saved!");
  status.html("Options Saved.");
  status.css("display","block");
  setTimeout(function() {
  	$("#status").fadeOut();
  }, 750);

}
// Restores select box state to saved value from localStorage.
function restore_options() {
  var warcSRC = localStorage["warcSRC"];
  var waybackWarcSource = document.getElementById("waybackWarcSource");
  if (!warcSRC || warcSRC == "") {
	waybackWarcSource.value = "C:\\xampp\\tomcat\\webapps\\ROOT\\files1\\"; //TODO, remove this obsolete code!
    return;
  }

  var handling;
  if(localStorage["handlingMethod"] == "save"){
	document.getElementById('output_save').checked = "checked";
	document.getElementById('output_display').removeAttribute('checked');
  }
  else if(localStorage["handlingMethod"] == "display"){
	document.getElementById('output_save').removeAttribute('checked');
	document.getElementById('output_display').checked = "checked";
  }

  waybackWarcSource.value = warcSRC;
}

function clear_options(){
	localStorage["warcSRC"] = "";
	restore_options();
	document.getElementById("waybackWarcSource").value = "sdf";
}

function checkURI(uri){
	var req=new XMLHttpRequest();
	try{
		req.open("GET", uri, false);
		req.send(null);
		return req.status;
	}catch(e){
		return -1;
	}
}

window.onload = function(){
	$('#postGeneration_save').on('click',function(){
		$('#uploadTo').attr("disabled","disabled");
		$('#filenameScheme').removeAttr("disabled");
		$("#exampleFileName").show();
	});
	$('#postGeneration_upload').on('click',function(){
		$('#uploadTo').removeAttr("disabled");
		$('#filenameScheme').attr("disabled","disabled");
		$("#exampleFileName").hide();

	});
	$("#restoreDefaults").on('click',function(){
		$("#uploadTo").val("http://warcreate.com/warcs").attr("disabled","disabled");
		$('#postGeneration_save').prop("checked","checked");
		$('#postGeneration_upload').removeAttr("checked");
		$('#filenameScheme').val("YYYYMMDDHHMMssSSS");
	});
	$('#save').on("click",function(){
		var uploadToURI;
		var filenameScheme;
		if($('#postGeneration_upload').prop("checked")){
			uploadToURI = $("#uploadTo").val();
			filenameScheme = "";
		}else {
			uploadToURI = "";
			filenameScheme = $("#filenameScheme").val();
		}

		localStorage['uploadTo'] = uploadToURI;
		localStorage['filenameScheme'] = filenameScheme;
	});
	$('#filenameScheme').on("keyup",showFilenameExample); //bind example display to text field change

	function showFilenameExample(){ //when the file format scheme changes, update the example
		$("#exampleFileName").html("Example: "+moment().format($("#filenameScheme").val())+".warc");
	};

	if(localStorage['uploadTo'] && localStorage['uploadTo'].length > 0){
		$('#uploadTo').removeAttr("disabled");
		$('#postGeneration_upload').prop("checked","checked");
		$('#postGeneration_save').removeAttr("checked");

		// hide/disable the "save to downloads" options if the user's current setting is "upload to"
		$('#filenameScheme').attr("disabled","disabled");
		$('#exampleFileName').hide();
	}else if(localStorage['filenameScheme'] && localStorage['filenameScheme'].length > 0){
		$('#filenameScheme').val(localStorage['filenameScheme']);
	}

	showFilenameExample(); //fire the keyup event onload
	return;

	//below is old functionality
	document.getElementById('body').click = restore_options;
	document.getElementById('save').click = save_options;
	document.getElementById('clear').click = clear_options;
	var checks = document.getElementsByClassName('check');
	for(var i=0; i<checks.length; i++){
		if(checkURI(checks[i].title) == 200){checks[i].className += " green"; checks[i].innerHTML = "✓ " + checks[i].innerHTML}
		else if(checkURI(checks[i].title) == 403){checks[i].className += " orange"; checks[i].innerHTML = "? " + checks[i].innerHTML}
		else{checks[i].className += " red"; checks[i].innerHTML = "✗ " + checks[i].innerHTML}
	}

};<commit_msg>Made options submit button responsive as to whether a change has actually been made, now affects its disabled attribute.<commit_after>// Saves options to localStorage.
function save_options() {
  var waybackWarcSource = document.getElementById('waybackWarcSource').value;
  var handling;
  if(document.getElementById('output_save').checked == "checked"){handling = "save";}
  else if(document.getElementById('output_display').checked == "checked"){handling = "display";}
  localStorage["warcSRC"] = waybackWarcSource;
  localStorage["handlingMethod"] = handling;
  // Update status to let user know options were saved.
  var status = $("#status");
  alert("status saved!");
  status.html("Options Saved.");
  status.css("display","block");
  setTimeout(function() {
  	$("#status").fadeOut();
  }, 750);

}
// Restores select box state to saved value from localStorage.
function restore_options() {
  var warcSRC = localStorage["warcSRC"];
  var waybackWarcSource = document.getElementById("waybackWarcSource");
  if (!warcSRC || warcSRC == "") {
	waybackWarcSource.value = "C:\\xampp\\tomcat\\webapps\\ROOT\\files1\\"; //TODO, remove this obsolete code!
    return;
  }

  var handling;
  if(localStorage["handlingMethod"] == "save"){
	document.getElementById('output_save').checked = "checked";
	document.getElementById('output_display').removeAttribute('checked');
  }
  else if(localStorage["handlingMethod"] == "display"){
	document.getElementById('output_save').removeAttribute('checked');
	document.getElementById('output_display').checked = "checked";
  }

  waybackWarcSource.value = warcSRC;
}

function clear_options(){
	localStorage["warcSRC"] = "";
	restore_options();
	document.getElementById("waybackWarcSource").value = "sdf";
}

function checkURI(uri){
	var req=new XMLHttpRequest();
	try{
		req.open("GET", uri, false);
		req.send(null);
		return req.status;
	}catch(e){
		return -1;
	}
}

var lastSavedStateString = ""; //string representation of the last saved state of the form inputs
function setSaveChangesButtonEnabledBasedOnOptionsChange(){
	if(lastSavedStateString != ""){
		var currentSavedState = $("#filenameScheme").val()+$("#uploadTo").val()+$("#postGeneration_save").is(':checked')+$("#postGeneration_upload").is(':checked');
		console.log(currentSavedState);
		if(lastSavedStateString == currentSavedState){
			$("#save").attr("disabled","disabled");
			console.log("disabling");
		}else {
			$("#save").removeAttr("disabled");
			console.log("enabling");
		}
		console.log("foo");
	}else{	//set the initial state
		lastSavedStateString = $("#filenameScheme").val()+$("#uploadTo").val()+$("#postGeneration_save").is(':checked')+$("#postGeneration_upload").is(':checked');
		$("#save").attr("disabled","disabled");
		$("#filenameScheme").on('keyup',setSaveChangesButtonEnabledBasedOnOptionsChange);
		$("#uploadTo").on('keyup',setSaveChangesButtonEnabledBasedOnOptionsChange);
		$("#postGeneration_save").on('click',setSaveChangesButtonEnabledBasedOnOptionsChange);
		$("#postGeneration_upload").on('click',setSaveChangesButtonEnabledBasedOnOptionsChange);
	}
}

window.onload = function(){
	$('#postGeneration_save').on('click',function(){
		$('#uploadTo').attr("disabled","disabled");
		$('#filenameScheme').removeAttr("disabled");
		$("#exampleFileName").show();
	});
	$('#postGeneration_upload').on('click',function(){
		$('#uploadTo').removeAttr("disabled");
		$('#filenameScheme').attr("disabled","disabled");
		$("#exampleFileName").hide();

	});
	$("#restoreDefaults").on('click',function(){
		$("#uploadTo").val("http://warcreate.com/warcs").attr("disabled","disabled");
		$('#postGeneration_save').prop("checked","checked");
		$('#postGeneration_upload').removeAttr("checked");
		$('#filenameScheme').val("YYYYMMDDHHMMssSSS");
	});
	$('#save').on("click",function(){
		var uploadToURI;
		var filenameScheme;
		if($('#postGeneration_upload').prop("checked")){
			uploadToURI = $("#uploadTo").val();
			filenameScheme = "";
		}else {
			uploadToURI = "";
			filenameScheme = $("#filenameScheme").val();
		}

		localStorage['uploadTo'] = uploadToURI;
		localStorage['filenameScheme'] = filenameScheme;
		//TODO: give feedback that options have been saved
	});
	$('#filenameScheme').on("keyup",showFilenameExample); //bind example display to text field change

	function showFilenameExample(){ //when the file format scheme changes, update the example
		$("#exampleFileName").html("Example: "+moment().format($("#filenameScheme").val())+".warc");
	};

	if(localStorage['uploadTo'] && localStorage['uploadTo'].length > 0){
		$('#uploadTo').removeAttr("disabled");
		$('#postGeneration_upload').prop("checked","checked");
		$('#postGeneration_save').removeAttr("checked");

		// hide/disable the "save to downloads" options if the user's current setting is "upload to"
		$('#filenameScheme').attr("disabled","disabled");
		$('#exampleFileName').hide();
	}else if(localStorage['filenameScheme'] && localStorage['filenameScheme'].length > 0){
		$('#filenameScheme').val(localStorage['filenameScheme']);
	}

	setSaveChangesButtonEnabledBasedOnOptionsChange(); //set enabled status of the save button initially
	showFilenameExample(); //fire the keyup event onload
	return;

	//below is old functionality
	document.getElementById('body').click = restore_options;
	document.getElementById('save').click = save_options;
	document.getElementById('clear').click = clear_options;
	var checks = document.getElementsByClassName('check');
	for(var i=0; i<checks.length; i++){
		if(checkURI(checks[i].title) == 200){checks[i].className += " green"; checks[i].innerHTML = "✓ " + checks[i].innerHTML}
		else if(checkURI(checks[i].title) == 403){checks[i].className += " orange"; checks[i].innerHTML = "? " + checks[i].innerHTML}
		else{checks[i].className += " red"; checks[i].innerHTML = "✗ " + checks[i].innerHTML}
	}

};<|endoftext|>