<commit_before>'use strict';

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

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

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

module.exports = class gateio extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'gateio',
            'name': 'Gate.io',
            'countries': 'CN',
            'version': '2',
            'rateLimit': 1000,
            'has': {
                'CORS': false,
                'createMarketOrder': false,
                'fetchTickers': true,
                'withdraw': true,
                'createDepositAddress': true,
                'fetchDepositAddress': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg',
                'api': {
                    'public': 'https://data.gate.io/api',
                    'private': 'https://data.gate.io/api',
                },
                'www': 'https://gate.io/',
                'doc': 'https://gate.io/api2',
                'fees': [
                    'https://gate.io/fee',
                    'https://support.gate.io/hc/en-us/articles/115003577673',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'pairs',
                        'marketinfo',
                        'marketlist',
                        'tickers',
                        'ticker/{id}',
                        'orderBook/{id}',
                        'trade/{id}',
                        'tradeHistory/{id}',
                        'tradeHistory/{id}/{tid}',
                    ],
                },
                'private': {
                    'post': [
                        'balances',
                        'depositAddress',
                        'newAddress',
                        'depositsWithdrawals',
                        'buy',
                        'sell',
                        'cancelOrder',
                        'cancelAllOrders',
                        'getOrder',
                        'openOrders',
                        'tradeHistory',
                        'withdraw',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': true,
                    'percentage': true,
                    'maker': 0.002,
                    'taker': 0.002,
                },
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetMarketinfo ();
        let markets = this.safeValue (response, 'pairs');
        if (!markets)
            throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response');
        let result = [];
        for (let i = 0; i < markets.length; i++) {
            let market = markets[i];
            let keys = Object.keys (market);
            let id = keys[0];
            let details = market[id];
            let [ base, quote ] = id.split ('_');
            base = base.toUpperCase ();
            quote = quote.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let precision = {
                'amount': 8,
                'price': details['decimal_places'],
            };
            let amountLimits = {
                'min': details['min_amount'],
                'max': undefined,
            };
            let priceLimits = {
                'min': Math.pow (10, -details['decimal_places']),
                'max': undefined,
            };
            let costLimits = {
                'min': amountLimits['min'] * priceLimits['min'],
                'max': undefined,
            };
            let limits = {
                'amount': amountLimits,
                'price': priceLimits,
                'cost': costLimits,
            };
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
                'maker': details['fee'] / 100,
                'taker': details['fee'] / 100,
                'precision': precision,
                'limits': limits,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balance = await this.privatePostBalances ();
        let result = { 'info': balance };
        let currencies = Object.keys (this.currencies);
        for (let i = 0; i < currencies.length; i++) {
            let currency = currencies[i];
            let code = this.commonCurrencyCode (currency);
            let account = this.account ();
            if ('available' in balance) {
                if (currency in balance['available']) {
                    account['free'] = parseFloat (balance['available'][currency]);
                }
            }
            if ('locked' in balance) {
                if (currency in balance['locked']) {
                    account['used'] = parseFloat (balance['locked'][currency]);
                }
            }
            account['total'] = this.sum (account['free'], account['used']);
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderBookId (this.extend ({
            'id': this.marketId (symbol),
        }, params));
        return this.parseOrderBook (orderbook);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        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, 'high24hr'),
            'low': this.safeFloat (ticker, 'low24hr'),
            'bid': this.safeFloat (ticker, 'highestBid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'lowestAsk'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': this.safeFloat (ticker, 'percentChange'),
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'quoteVolume'),
            'quoteVolume': this.safeFloat (ticker, 'baseVolume'),
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTickers (params);
        let result = {};
        let ids = Object.keys (tickers);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let [ baseId, quoteId ] = id.split ('_');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let ticker = tickers[id];
            let market = undefined;
            if (symbol in this.markets)
                market = this.markets[symbol];
            if (id in this.markets_by_id)
                market = this.markets_by_id[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.publicGetTickerId (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        // exchange reports local time (UTC+8)
        let timestamp = this.parse8601 (trade['date']) - 8 * 60 * 60 * 1000;
        return {
            'id': trade['tradeID'],
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': trade['type'],
            'price': this.safeFloat (trade, 'rate'),
            'amount': this.safeFloat (trade, 'amount'),
        };
    }

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

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market')
            throw new ExchangeError (this.id + ' allows limit orders only');
        await this.loadMarkets ();
        let method = 'privatePost' + this.capitalize (side);
        let order = {
            'currencyPair': this.marketId (symbol),
            'rate': price,
            'amount': amount,
        };
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['orderNumber'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostCancelOrder ({ 'orderNumber': id });
    }

    async queryDepositAddress (method, currency, params = {}) {
        method = 'privatePost' + method + 'Address';
        let response = await this[method] (this.extend ({
            'currency': currency,
        }, params));
        let address = undefined;
        if ('addr' in response)
            address = this.safeString (response, 'addr');
        if ((typeof address !== 'undefined') && (address.indexOf ('address') >= 0))
            throw new InvalidAddress (this.id + ' queryDepositAddress ' + address);
        return {
            'currency': currency,
            'address': address,
            'status': (typeof address !== 'undefined') ? 'ok' : 'none',
            'info': response,
        };
    }

    async createDepositAddress (currency, params = {}) { // CHANGE
        return await this.queryDepositAddress ('New', currency, params);
    }

    async fetchDepositAddress (currency, params = {}) {  // CHANGE
        return await this.queryDepositAddress ('Deposit', currency, params);
    }

    async withdraw (currency, amount, address, tag = undefined, params = {}) {
        this.checkAddress (address);
        await this.loadMarkets ();
        let response = await this.privatePostWithdraw (this.extend ({
            'currency': currency.toLowerCase (),
            'amount': amount,
            'address': address, // Address must exist in you AddressBook in security settings
        }, params));
        return {
            'info': response,
            'id': undefined,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let prefix = (api === 'private') ? (api + '/') : '';
        let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length)
                url += '?' + this.urlencode (query);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            let request = { 'nonce': nonce };
            body = this.urlencode (this.extend (request, query));
            let signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512');
            headers = {
                'Key': this.apiKey,
                'Sign': signature,
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        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 ('result' in response) {
            let result = response['result'];
            let message = this.id + ' ' + this.json (response);
            if (typeof result === 'undefined')
                throw new ExchangeError (message);
            if (typeof result === 'string') {
                if (result !== 'true')
                    throw new ExchangeError (message);
            } else if (!result) {
                throw new ExchangeError (message);
            }
        }
        return response;
    }
};
<commit_msg>gateio: add fetchOrder, modify cancelOrder, createOrder, queryDeposit, add handleErrors based on initial work by @lfern<commit_after>'use strict';

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

const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidAddress, OrderNotFound, NotSupported, DDoSProtection, InsufficientFunds } = require ('./base/errors');

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

module.exports = class gateio extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'gateio',
            'name': 'Gate.io',
            'countries': 'CN',
            'version': '2',
            'rateLimit': 1000,
            'has': {
                'CORS': false,
                'createMarketOrder': false,
                'fetchTickers': true,
                'withdraw': true,
                'createDepositAddress': true,
                'fetchDepositAddress': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg',
                'api': {
                    'public': 'https://data.gate.io/api',
                    'private': 'https://data.gate.io/api',
                },
                'www': 'https://gate.io/',
                'doc': 'https://gate.io/api2',
                'fees': [
                    'https://gate.io/fee',
                    'https://support.gate.io/hc/en-us/articles/115003577673',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'pairs',
                        'marketinfo',
                        'marketlist',
                        'tickers',
                        'ticker/{id}',
                        'orderBook/{id}',
                        'trade/{id}',
                        'tradeHistory/{id}',
                        'tradeHistory/{id}/{tid}',
                    ],
                },
                'private': {
                    'post': [
                        'balances',
                        'depositAddress',
                        'newAddress',
                        'depositsWithdrawals',
                        'buy',
                        'sell',
                        'cancelOrder',
                        'cancelAllOrders',
                        'getOrder',
                        'openOrders',
                        'tradeHistory',
                        'withdraw',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': true,
                    'percentage': true,
                    'maker': 0.002,
                    'taker': 0.002,
                },
            },
            'exceptions': {
                '4': DDoSProtection,
                '7': NotSupported,
                '8': NotSupported,
                '9': NotSupported,
                '15': DDoSProtection,
                '16': OrderNotFound,
                '17': OrderNotFound,
                '21': InsufficientFunds,
            },
            // https://gate.io/api2#errCode
            'errorCodeNames': {
                '1': 'Invalid request',
                '2': 'Invalid version',
                '3': 'Invalid request',
                '4': 'Too many attempts',
                '5': 'Invalid sign',
                '6': 'Invalid sign',
                '7': 'Currency is not supported',
                '8': 'Currency is not supported',
                '9': 'Currency is not supported',
                '10': 'Verified failed',
                '11': 'Obtaining address failed',
                '12': 'Empty params',
                '13': 'Internal error, please report to administrator',
                '14': 'Invalid user',
                '15': 'Cancel order too fast, please wait 1 min and try again',
                '16': 'Invalid order id or order is already closed',
                '17': 'Invalid orderid',
                '18': 'Invalid amount',
                '19': 'Not permitted or trade is disabled',
                '20': 'Your order size is too small',
                '21': 'You don\'t have enough fund',
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetMarketinfo ();
        let markets = this.safeValue (response, 'pairs');
        if (!markets)
            throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response');
        let result = [];
        for (let i = 0; i < markets.length; i++) {
            let market = markets[i];
            let keys = Object.keys (market);
            let id = keys[0];
            let details = market[id];
            let [ base, quote ] = id.split ('_');
            base = base.toUpperCase ();
            quote = quote.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let precision = {
                'amount': 8,
                'price': details['decimal_places'],
            };
            let amountLimits = {
                'min': details['min_amount'],
                'max': undefined,
            };
            let priceLimits = {
                'min': Math.pow (10, -details['decimal_places']),
                'max': undefined,
            };
            let costLimits = {
                'min': amountLimits['min'] * priceLimits['min'],
                'max': undefined,
            };
            let limits = {
                'amount': amountLimits,
                'price': priceLimits,
                'cost': costLimits,
            };
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
                'maker': details['fee'] / 100,
                'taker': details['fee'] / 100,
                'precision': precision,
                'limits': limits,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balance = await this.privatePostBalances ();
        let result = { 'info': balance };
        let currencies = Object.keys (this.currencies);
        for (let i = 0; i < currencies.length; i++) {
            let currency = currencies[i];
            let code = this.commonCurrencyCode (currency);
            let account = this.account ();
            if ('available' in balance) {
                if (currency in balance['available']) {
                    account['free'] = parseFloat (balance['available'][currency]);
                }
            }
            if ('locked' in balance) {
                if (currency in balance['locked']) {
                    account['used'] = parseFloat (balance['locked'][currency]);
                }
            }
            account['total'] = this.sum (account['free'], account['used']);
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderBookId (this.extend ({
            'id': this.marketId (symbol),
        }, params));
        return this.parseOrderBook (orderbook);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        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, 'high24hr'),
            'low': this.safeFloat (ticker, 'low24hr'),
            'bid': this.safeFloat (ticker, 'highestBid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'lowestAsk'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': this.safeFloat (ticker, 'percentChange'),
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'quoteVolume'),
            'quoteVolume': this.safeFloat (ticker, 'baseVolume'),
            'info': ticker,
        };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if (body.length <= 0) {
            return;
        }
        if (body[0] !== '{') {
            return;
        }
        let jsonbodyParsed = JSON.parse (body);
        let resultString = this.safeString (jsonbodyParsed, 'result', '');
        if (resultString !== 'false') {
            return;
        }
        let errorCode = this.safeString (jsonbodyParsed, 'code');
        if (typeof errorCode !== 'undefined') {
            const exceptions = this.exceptions;
            const errorCodeNames = this.errorCodeNames;
            if (errorCode in exceptions) {
                let message = '';
                if (errorCode in errorCodeNames) {
                    message = errorCodeNames[errorCode];
                } else {
                    message = this.safeString (jsonbodyParsed, 'message', '(unknown)');
                }
                throw new exceptions[errorCode] (message);
            }
        }
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTickers (params);
        let result = {};
        let ids = Object.keys (tickers);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let [ baseId, quoteId ] = id.split ('_');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let ticker = tickers[id];
            let market = undefined;
            if (symbol in this.markets)
                market = this.markets[symbol];
            if (id in this.markets_by_id)
                market = this.markets_by_id[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.publicGetTickerId (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        // exchange reports local time (UTC+8)
        let timestamp = this.parse8601 (trade['date']) - 8 * 60 * 60 * 1000;
        return {
            'id': trade['tradeID'],
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': trade['type'],
            'price': this.safeFloat (trade, 'rate'),
            'amount': this.safeFloat (trade, 'amount'),
        };
    }

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

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        let response = await this.privatePostOpenOrders ();
        if (typeof response !== 'undefined') {
            if ('result' in response) {
                if ('orders' in response) {
                    let responseOrders = response['orders'];
                    let resultOrders = [];
                    for (let i = 0; i < responseOrders.length; i++) {
                        resultOrders.push (this.parseOrder (responseOrders[i]));
                    }
                    return resultOrders;
                }
            }
        }
        let defaultUnparsedResult = {};
        defaultUnparsedResult['info'] = response;
        return defaultUnparsedResult;
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostGetOrder (this.extend ({
            'orderNumber': id,
            'currencyPair': this.marketId (symbol),
        }, params));
        return this.parseOrder (response['order']);
    }

    parseOrderStatus (status) {
        let statuses = {
            'cancelled': 'canceled',
            'closed': 'closed',
            'open': 'open',
        };
        if (status in statuses)
            return statuses[status];
        return status;
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (!market)
            market = this.safeValue (this.marketsById, order['currencyPair']);
        if (market)
            symbol = market['symbol'];
        let timestamp = this.safeInteger (order, 'timestamp') * 1000;
        let price = this.safeFloat (order, 'filledRate');
        let amount = this.safeFloat (order, 'initialAmount');
        let filled = this.safeFloat (order, 'filledAmount');
        let remaining = this.safeFloat (order, 'amount');
        let status = this.parseOrderStatus (this.safeString (order, 'status'));
        let feeCost = this.safeFloat (order, 'feeValue');
        let feeCurrency = this.safeString (order, 'feeCurrency');
        if (typeof feeCurrency !== 'undefined') {
            if (feeCurrency in this.currencies_by_id) {
                feeCurrency = this.currencies_by_id[feeCurrency]['code'];
            }
        }
        return {
            'id': this.safeString (order, 'orderNumber'),
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'status': status,
            'symbol': symbol,
            'type': 'limit',
            'side': this.safeString (order, 'type'),
            'price': price,
            'cost': undefined,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'trades': undefined,
            'fee': {
                'cost': feeCost,
                'currency': feeCurrency,
            },
            'info': this.safeValue (order, 'info', order),
        };
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market')
            throw new ExchangeError (this.id + ' allows limit orders only');
        await this.loadMarkets ();
        let method = 'privatePost' + this.capitalize (side);
        let order = {
            'currencyPair': this.marketId (symbol),
            'rate': price,
            'amount': amount,
        };
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['orderNumber'].toString (),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostCancelOrder ({
            'orderNumber': id,
            'currencyPair': this.marketId (symbol),
        });
    }

    async queryDepositAddress (method, currency, params = {}) {
        method = 'privatePost' + method + 'Address';
        let response = await this[method] (this.extend ({
            'currency': currency,
        }, params));
        let address = undefined;
        let tag = undefined;
        if ('addr' in response) {
            address = this.safeString (response, 'addr');
            if (currency === 'XRP') {
                let parts = address.split ('/', 2);
                address = parts[0];
                tag = parts[1];
            }
        }
        if ((typeof address !== 'undefined') && (address.indexOf ('address') >= 0))
            throw new InvalidAddress (this.id + ' queryDepositAddress ' + address);
        return {
            'currency': currency,
            'address': address,
            'tag': tag,
            'status': (typeof address !== 'undefined') ? 'ok' : 'none',
            'info': response,
        };
    }

    async createDepositAddress (currency, params = {}) { // CHANGE
        return await this.queryDepositAddress ('New', currency, params);
    }

    async fetchDepositAddress (currency, params = {}) {  // CHANGE
        return await this.queryDepositAddress ('Deposit', currency, params);
    }

    async withdraw (currency, amount, address, tag = undefined, params = {}) {
        this.checkAddress (address);
        await this.loadMarkets ();
        let response = await this.privatePostWithdraw (this.extend ({
            'currency': currency.toLowerCase (),
            'amount': amount,
            'address': address, // Address must exist in you AddressBook in security settings
        }, params));
        return {
            'info': response,
            'id': undefined,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let prefix = (api === 'private') ? (api + '/') : '';
        let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length)
                url += '?' + this.urlencode (query);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            let request = { 'nonce': nonce };
            body = this.urlencode (this.extend (request, query));
            let signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512');
            headers = {
                'Key': this.apiKey,
                'Sign': signature,
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        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 ('result' in response) {
            let result = response['result'];
            let message = this.id + ' ' + this.json (response);
            if (typeof result === 'undefined')
                throw new ExchangeError (message);
            if (typeof result === 'string') {
                if (result !== 'true')
                    throw new ExchangeError (message);
            } else if (!result) {
                throw new ExchangeError (message);
            }
        }
        return response;
    }
};
<|endoftext|>


<commit_before>"use strict";

let gulp = require('gulp');
let connect = require('gulp-connect');
let headerComment = require('gulp-header-comment');

let uglify = require("gulp-uglify"); // for minify es5 codes
let uglifyEs = require('uglify-es'); //  `uglify-es` for ES6 support
let composer = require('gulp-uglify/composer');
let minify = composer(uglifyEs, console);

let util = require('gulp-util');
let rename = require('gulp-rename');

let webpack = require("webpack-stream");
let webpackConfig = require('./webpack.config.js');

let pkg = require('./package.json');
let srcDir = './src/';
let buildDir = './public/';
let testDir = './test/';

gulp.task('build', buildTask);
gulp.task('buildTaskWithTranspiler', buildTaskWithTranspiler);
gulp.task('watch', watchTask);
gulp.task('release', ['buildTaskWithTranspiler', 'watch']);
gulp.task('default', ['build', 'watch']);

let header = `
SmartChartsNXT
http://www.smartcharts.cf
Version:${pkg.version}

Copyright 2017 Kausik Dey
Released under the MIT license
https://github.com/kausikongit/smartChartsNXT/blob/develop/LICENSE
`;

/*
 * Generate a build based on the source file
 */

function buildTask() {
  return gulp.src(srcDir + 'build.core.js')
    .pipe(webpack(webpackConfig))
    .pipe(rename('smartChartsNXT.bundle.js'))
    .pipe(insert.prepend(header))
    .pipe(gulp.dest(buildDir))
    .pipe(minify({}))
    .pipe(rename('smartChartsNXT.bundle.min.js'))
    .pipe(insert.prepend(header))
    .pipe(gulp.dest(buildDir));
}

function watchTask() {
  return gulp.watch('./src/**', ['build']);
}<commit_msg>Minor fix<commit_after>"use strict";

let gulp = require('gulp');
let insert = require('gulp-insert');
let connect = require('gulp-connect');

let uglifyEs = require('uglify-es'); //  `uglify-es` for ES6 support
let composer = require('gulp-uglify/composer');
let minify = composer(uglifyEs, console);

let util = require('gulp-util');
let rename = require('gulp-rename');

let webpack = require("webpack-stream");
let webpackConfig = require('./webpack.config.js');

let pkg = require('./package.json');
let srcDir = './src/';
let buildDir = './public/';
let testDir = './test/';

gulp.task('build', buildTask);
gulp.task('watch', watchTask);
gulp.task('default', ['build', 'watch']);

let header = `/**
* SmartChartsNXT
* http://www.smartcharts.cf
* Version:${pkg.version}
*
* Copyright 2017 Kausik Dey
* Released under the MIT license
* https://github.com/kausikongit/smartChartsNXT/blob/develop/LICENSE
*/
`;

/*
 * Generate a build based on the source file
 */

function buildTask() {
  return gulp.src(srcDir + 'build.core.js')
    .pipe(webpack(webpackConfig))
    .pipe(rename('smartChartsNXT.bundle.js'))
    .pipe(insert.prepend(header))
    .pipe(gulp.dest(buildDir))
    .pipe(minify({}))
    .pipe(rename('smartChartsNXT.bundle.min.js'))
    .pipe(insert.prepend(header))
    .pipe(gulp.dest(buildDir));
}

function watchTask() {
  return gulp.watch('./src/**', ['build']);
}<|endoftext|>


<commit_before>var axios = require('axios');

var id = "YOUR_CLIENT_ID";
var sec = "YOUR_SECRET_ID";
var param = "?client_id" + id + "&client_secret=" + sec;

function getUserInfo (username) {
    return axios.get('https://api.github.com/users/' + username + param);
}

function getRepos (username) {
  // fetch username repos
  return axios.get('https://api.github.com/users/' + username + '/repos' + param + '&per_page=100');
}

function getTotalStars (repos) {
  // calculate all the stars that the user has
  return repos.data.reduce(function(prev, current){
    return prev + current.stargazers_count
  }, 0)
}

function getPlayersData (player) {
  // get repos
  // getTotalStars
  // return object with that data
  return getRepos(player.login)
    .then(getTotalStars)
    .then(function (totalStars) {
      return {
        followers: player.followers,
        totalStars: totalStars
      }
    })
}

function calculateScores (players) {
  // return an array, after doing some fancy algorithm to determine a winner
  return [
    players[0].followers * 3 + players[0].totalStars,
    players[1].followers * 3 + players[1].totalStars
  ]
}

var helpers = {
  getPlayersInfo: function(players) {
    return axios.all(players.map(function (username) {
      return getUserInfo(username)
    })).then(function (info) {
      return info.map(function (user) {
        return user.data;
      })
    }).catch(function (err) {
      console.warn('Error in getPlayersInfo', err)
    })
  },
  battle: function (players) {
    var playerOneData = getPlayersData(players[0]);
    var playerTwoData = getPlayersData(players[1]);
    return axios.all([playerOneData, playerTwoData])
      .then(calculateScores)
      .catch(function(err) {console.warn("Error in getPlayersInfo: ", err)})
  }
};

module.exports = helpers;
<commit_msg>Refactor helpers<commit_after>import axios from 'axios'

const id = "YOUR_CLIENT_ID";
const sec = "YOUR_SECRET_ID";
const param = "?client_id" + id + "&client_secret=" + sec;

function getUserInfo (username) {
    return axios.get('https://api.github.com/users/' + username + param);
}

function getRepos (username) {
  // fetch username repos
  return axios.get('https://api.github.com/users/' + username + '/repos' + param + '&per_page=100');
}

function getTotalStars (repos) {
  // calculate all the stars that the user has
  return repos.data.reduce(function(prev, current){
    return prev + current.stargazers_count
  }, 0)
}

function getPlayersData (player) {
  // get repos
  // getTotalStars
  // return object with that data
  return getRepos(player.login)
    .then(getTotalStars)
    .then(function (totalStars) {
      return {
        followers: player.followers,
        totalStars: totalStars
      }
    })
}

function calculateScores (players) {
  // return an array, after doing some fancy algorithm to determine a winner
  return [
    players[0].followers * 3 + players[0].totalStars,
    players[1].followers * 3 + players[1].totalStars
  ]
}

export function getPlayersInfo (players) {
  return axios.all(players.map(function (username) {
    return getUserInfo(username)
  })).then(function (info) {
    return info.map(function (user) {
      return user.data;
    })
  }).catch(function (err) {
    console.warn('Error in getPlayersInfo', err)
  })
}

export function battle (players) {
  const playerOneData = getPlayersData(players[0]);
  const playerTwoData = getPlayersData(players[1]);
  return axios.all([playerOneData, playerTwoData])
    .then(calculateScores)
    .catch(function(err) {console.warn("Error in getPlayersInfo: ", err)})
}
<|endoftext|>


<commit_before>import DatatableFactory from 'ember-easy-datatable/utils/datatable-factory'
import hbs from 'htmlbars-inline-precompile'
import { module, test } from 'qunit'
import { setupRenderingTest } from 'ember-qunit'
import { render } from '@ember/test-helpers'

import '@ember/test-helpers'

import customHelpers from '../../helpers/_custom-helpers'

module('Integration | Component | content edition', function (hooks) {
  setupRenderingTest(hooks)

  hooks.beforeEach(function () {
    this.set(
      'table',
      DatatableFactory.makeDatatable({
        headers: ['', 'Name', 'Value 1', 'Value 2', 'Value 3'],
        body: [
          [{ isHeader: true, value: '#0' }, 'Row 0', 0, 10, 20],
          [{ isHeader: true, value: '#1' }, 'Row 1', 1, 11, 21],
          [{ isHeader: true, value: '#2' }, 'Row 2', 2, 12, 22],
          [{ isHeader: true, value: '#3' }, 'Row 3', 3, 13, 23],
        ],
      })
    )
  })

  test('Click and edit', async function (assert) {
    assert.expect(5)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)

    assert.ok(this.$('input').length === 1, 'Editor is displayed')
    assert.equal(this.$('input').get(0).value, 'Row 0', 'Editor has the correct content')

    await customHelpers.typeInDatatable('This is my row')
    await customHelpers.pressEnterInDatatable()
    await customHelpers.clickOnDatatableColumnCell(1)

    assert.ok(this.$('input').length === 1, 'Editor is displayed')

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['This is my row', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )
  })

  test('Cell validation is called only once when pressing Enter key', async function (assert) {
    var count = 0
    assert.expect(3)

    this.table.reopen({
      validateCell: function () {
        count += 1
        return true
      },
    })

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.typeInDatatable('This is my row')
    await customHelpers.pressEnterInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['This is my row', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'After changing a cell value, the datatable is updated'
    )

    assert.strictEqual(count, 1, 'and validation is called only once')
  })

  test('Cell validation is not called at all when pressing Escape key', async function (assert) {
    var count = 0
    assert.expect(3)

    this.table.reopen({
      countValidateCell: function () {
        count += 1
        return true
      },
    })

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.typeInDatatable('This is my row')
    await customHelpers.pressEscInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'After canceling a cell edition, the datatable is back to its original values'
    )

    assert.strictEqual(count, 0, 'and validation is not called at all')
  })

  test('cell validation is not called at all if not modified', async function (assert) {
    var count = 0
    assert.expect(4)

    this.table.reopen({
      countValidateCell: function () {
        count += 1
        return true
      },
    })

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 0, 'Editor is not displayed')

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'without editing, the datatable is the same!'
    )

    assert.strictEqual(count, 0, 'and validation is not called at all')
  })

  test('Clicking moves the editor', async function (assert) {
    assert.expect(2)

    await render(hbs`{{easy-datatable table=table}}`)

    await customHelpers.clickOnDatatableValueCell(1, 1)
    assert.ok(this.$('input').length === 1, 'Editor is displayed')

    await customHelpers.clickOnDatatableValueCell(3, 3)
    assert.ok(this.$('input').length === 1, 'Editor is displayed')
  })

  test('Navigate, press enter and edit', async function (assert) {
    assert.expect(4)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)

    await customHelpers.pressEscInDatatable()
    await customHelpers.pressRightKeyInDatatable()
    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 1, 'Editor is displayed')

    await customHelpers.typeInDatatable('My new value')
    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 0, 'Editor is not displayed')
    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'My new value', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )
  })

  test('Navigate, start typing to replace the cell content', async function (assert) {
    assert.expect(4)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.pressEscInDatatable()
    await customHelpers.typeInDatatable('I type something without having an input')

    assert.ok(this.$('input').length === 1, 'Editor is displayed')

    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 0, 'Editor is not displayed')

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['I type something without having an input', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )
  })

  test('Cells with "isEditable" set to false can not be edited', async function (assert) {
    assert.expect(6)

    this.table.set('headers.cells.firstObject.isEditable', false)
    this.table.get('body').forEach(function (row, index) {
      row.get('cells')[index % 2 === 0 ? 1 : 0].set('isEditable', false)
    })

    await render(hbs`{{easy-datatable table=table}}`)

    await customHelpers.clickOnDatatableColumnCell(1)

    assert.ok(this.$('input').length === 0, 'When clicking on the protected cell, the editor does not show up')

    await customHelpers.clickOnDatatableColumnCell(2)

    assert.ok(this.$('input').length === 1, 'but it still work on an editable cell')

    await customHelpers.pressEscInDatatable()
    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 0, 'When pressing enter in a protected cell, we do not get the editor')

    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 1, 'but it still works in editable cells')

    await customHelpers.pressEscInDatatable()
    await customHelpers.pressLeftKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.ok(this.$('input').length === 0, 'Same principle when typing in a protected cell')

    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.typeInDatatable('Ho')

    assert.ok(this.$('input').length === 1, 'but it still works in editable cells')

    await customHelpers.pressEscInDatatable()
  })

  test('Navigation based on enter', async function (assert) {
    assert.expect(9)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(2, 2)
    await customHelpers.typeInDatatable('x')
    await customHelpers.pressEnterInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'x', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'After entering "x", the datatable content is correct'
    )

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 3, column: 2 },
      'If the cell value is validated using enter, then cell below is selected'
    )

    await customHelpers.typeInDatatable('y')
    await customHelpers.pressTabKeyInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'x', '11', '21'],
        ['Row 2', 'y', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 3, column: 3 },
      'If the cell value is validated using tab, then cell on the right is selected'
    )

    await customHelpers.typeInDatatable('z')
    await customHelpers.pressShiftTabKeyInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'x', '11', '21'],
        ['Row 2', 'y', 'z', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 3, column: 2 },
      'If the cell value is validated using shift+tab, then cell on the left is selected'
    )

    await customHelpers.pressDownKeyInDatatable()

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 4, column: 2 },
      'If the down key is used, then the down cell is selected'
    )

    await customHelpers.typeInDatatable('a')
    await customHelpers.pressEnterInDatatable()

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 4, column: 2 },
      'If the cell value is validated using enter but there is no new line, selection do not change'
    )
  })
})
<commit_msg>remove jquery from content-edition-test.js<commit_after>import DatatableFactory from 'ember-easy-datatable/utils/datatable-factory'
import hbs from 'htmlbars-inline-precompile'
import { module, test } from 'qunit'
import { setupRenderingTest } from 'ember-qunit'
import { render } from '@ember/test-helpers'

import '@ember/test-helpers'

import customHelpers from '../../helpers/_custom-helpers'

module('Integration | Component | content edition', function (hooks) {
  setupRenderingTest(hooks)

  hooks.beforeEach(function () {
    this.set(
      'table',
      DatatableFactory.makeDatatable({
        headers: ['', 'Name', 'Value 1', 'Value 2', 'Value 3'],
        body: [
          [{ isHeader: true, value: '#0' }, 'Row 0', 0, 10, 20],
          [{ isHeader: true, value: '#1' }, 'Row 1', 1, 11, 21],
          [{ isHeader: true, value: '#2' }, 'Row 2', 2, 12, 22],
          [{ isHeader: true, value: '#3' }, 'Row 3', 3, 13, 23],
        ],
      })
    )
  })

  test('Click and edit', async function (assert) {
    assert.expect(5)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)

    assert.dom('input').exists({ count: 1 }, 'Editor is displayed')
    assert.dom('input').hasValue('Row 0', 'Editor has the correct content')

    await customHelpers.typeInDatatable('This is my row')
    await customHelpers.pressEnterInDatatable()
    await customHelpers.clickOnDatatableColumnCell(1)

    assert.dom('input').exists({ count: 1 }, 'Editor is displayed')

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['This is my row', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )
  })

  test('Cell validation is called only once when pressing Enter key', async function (assert) {
    var count = 0
    assert.expect(3)

    this.table.reopen({
      validateCell: function () {
        count += 1
        return true
      },
    })

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.typeInDatatable('This is my row')
    await customHelpers.pressEnterInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['This is my row', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'After changing a cell value, the datatable is updated'
    )

    assert.strictEqual(count, 1, 'and validation is called only once')
  })

  test('Cell validation is not called at all when pressing Escape key', async function (assert) {
    var count = 0
    assert.expect(3)

    this.table.reopen({
      countValidateCell: function () {
        count += 1
        return true
      },
    })

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.typeInDatatable('This is my row')
    await customHelpers.pressEscInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'After canceling a cell edition, the datatable is back to its original values'
    )

    assert.strictEqual(count, 0, 'and validation is not called at all')
  })

  test('cell validation is not called at all if not modified', async function (assert) {
    var count = 0
    assert.expect(4)

    this.table.reopen({
      countValidateCell: function () {
        count += 1
        return true
      },
    })

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.pressEnterInDatatable()

    assert.dom('input').doesNotExist('Editor is not displayed')

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'without editing, the datatable is the same!'
    )

    assert.strictEqual(count, 0, 'and validation is not called at all')
  })

  test('Clicking moves the editor', async function (assert) {
    assert.expect(2)

    await render(hbs`{{easy-datatable table=table}}`)

    await customHelpers.clickOnDatatableValueCell(1, 1)
    assert.dom('input').exists('Editor is displayed')

    await customHelpers.clickOnDatatableValueCell(3, 3)
    assert.dom('input').exists('Editor is displayed')
  })

  test('Navigate, press enter and edit', async function (assert) {
    assert.expect(4)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)

    await customHelpers.pressEscInDatatable()
    await customHelpers.pressRightKeyInDatatable()
    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.dom('input').exists({ count: 1 }, 'Editor is displayed')

    await customHelpers.typeInDatatable('My new value')
    await customHelpers.pressEnterInDatatable()

    assert.dom('input').doesNotExist('Editor is not displayed')
    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'My new value', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )
  })

  test('Navigate, start typing to replace the cell content', async function (assert) {
    assert.expect(4)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(1, 1)
    await customHelpers.pressEscInDatatable()
    await customHelpers.typeInDatatable('I type something without having an input')

    assert.dom('input').exists({ count: 1 }, 'Editor is displayed')

    await customHelpers.pressEnterInDatatable()

    assert.dom('input').doesNotExist('Editor is not displayed')

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['I type something without having an input', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )
  })

  test('Cells with "isEditable" set to false can not be edited', async function (assert) {
    assert.expect(6)

    this.table.set('headers.cells.firstObject.isEditable', false)
    this.table.get('body').forEach(function (row, index) {
      row.get('cells')[index % 2 === 0 ? 1 : 0].set('isEditable', false)
    })

    await render(hbs`{{easy-datatable table=table}}`)

    await customHelpers.clickOnDatatableColumnCell(1)

    assert.dom('input').doesNotExist('When clicking on the protected cell, the editor does not show up')

    await customHelpers.clickOnDatatableColumnCell(2)

    assert.dom('input').exists({ count: 1 }, 'but it still work on an editable cell')

    await customHelpers.pressEscInDatatable()
    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.dom('input').doesNotExist('When pressing enter in a protected cell, we do not get the editor')

    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.dom('input').exists({ count: 1 }, 'but it still works in editable cells')

    await customHelpers.pressEscInDatatable()
    await customHelpers.pressLeftKeyInDatatable()
    await customHelpers.pressEnterInDatatable()

    assert.dom('input').doesNotExist('Same principle when typing in a protected cell')

    await customHelpers.pressDownKeyInDatatable()
    await customHelpers.typeInDatatable('Ho')

    assert.dom('input').exists({ count: 1 }, 'but it still works in editable cells')

    await customHelpers.pressEscInDatatable()
  })

  test('Navigation based on enter', async function (assert) {
    assert.expect(9)

    await render(hbs`{{easy-datatable table=table}}`)

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', '1', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    await customHelpers.clickOnDatatableValueCell(2, 2)
    await customHelpers.typeInDatatable('x')
    await customHelpers.pressEnterInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'x', '11', '21'],
        ['Row 2', '2', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'After entering "x", the datatable content is correct'
    )

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 3, column: 2 },
      'If the cell value is validated using enter, then cell below is selected'
    )

    await customHelpers.typeInDatatable('y')
    await customHelpers.pressTabKeyInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'x', '11', '21'],
        ['Row 2', 'y', '12', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 3, column: 3 },
      'If the cell value is validated using tab, then cell on the right is selected'
    )

    await customHelpers.typeInDatatable('z')
    await customHelpers.pressShiftTabKeyInDatatable()

    assert.deepEqual(
      customHelpers.getDatatableContent(),
      [
        ['Row 0', '0', '10', '20'],
        ['Row 1', 'x', '11', '21'],
        ['Row 2', 'y', 'z', '22'],
        ['Row 3', '3', '13', '23'],
      ],
      'The datatable content is correct'
    )

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 3, column: 2 },
      'If the cell value is validated using shift+tab, then cell on the left is selected'
    )

    await customHelpers.pressDownKeyInDatatable()

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 4, column: 2 },
      'If the down key is used, then the down cell is selected'
    )

    await customHelpers.typeInDatatable('a')
    await customHelpers.pressEnterInDatatable()

    assert.deepEqual(
      customHelpers.getSelectedPosition(),
      { row: 4, column: 2 },
      'If the cell value is validated using enter but there is no new line, selection do not change'
    )
  })
})
<|endoftext|>


<commit_before>export default {
  props: {
    filter: String,
    filterMethod: {
      type: Function,
      default (rows, terms, cols = this.computedCols, cellValue = this.getCellValue) {
        return rows.filter(
          row => cols.some(col => (cellValue(col, row) + '').toLowerCase().indexOf(terms) !== -1)
        )
      }
    }
  },
  computed: {
    hasFilter () {
      return this.filter !== void 0
    }
  },
  watch: {
    filter () {
      this.$nextTick(() => {
        this.setPagination({ page: 1 })
      })
    }
  }
}
<commit_msg>Fix: #1105 - Datatable fix sort lowercase (#1106)<commit_after>export default {
  props: {
    filter: String,
    filterMethod: {
      type: Function,
      default (rows, terms, cols = this.computedCols, cellValue = this.getCellValue) {
        const lowerTerms = terms ? terms.toLowerCase() : ''
        return rows.filter(
          row => cols.some(col => (cellValue(col, row) + '').toLowerCase().indexOf(lowerTerms) !== -1)
        )
      }
    }
  },
  computed: {
    hasFilter () {
      return this.filter !== void 0
    }
  },
  watch: {
    filter () {
      this.$nextTick(() => {
        this.setPagination({ page: 1 })
      })
    }
  }
}
<|endoftext|>


<commit_before>import { Events, Styler, UICorePlugin, template } from 'clappr'
import pluginHtml from './public/level-selector.html'
import pluginStyle from './public/style.scss'

const AUTO = -1

export default class LevelSelector extends UICorePlugin {

  static get version() { return VERSION }

  get name() { return 'level_selector' }
  get template() { return template(pluginHtml) }

  get attributes() {
    return {
      'class': this.name,
      'data-level-selector': ''
    }
  }

  get events() {
    return {
      'click [data-level-selector-select]': 'onLevelSelect',
      'click [data-level-selector-button]': 'onShowLevelSelectMenu'
    }
  }

  bindEvents() {
    this.listenTo(this.core, Events.CORE_READY, this.bindPlaybackEvents)
    this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.reload)
    this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this.render)
    this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu)
  }

  unBindEvents() {
    this.stopListening(this.core, Events.CORE_READY)
    this.stopListening(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED)
    this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED)
    this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE)
    this.stopListening(this.core.activePlayback, Events.PLAYBACK_LEVELS_AVAILABLE)
    this.stopListening(this.core.activePlayback, Events.PLAYBACK_LEVEL_SWITCH_START)
    this.stopListening(this.core.activePlayback, Events.PLAYBACK_LEVEL_SWITCH_END)
    this.stopListening(this.core.activePlayback, Events.PLAYBACK_BITRATE)
  }

  bindPlaybackEvents() {
    let currentPlayback = this.core.activePlayback

    this.listenTo(currentPlayback, Events.PLAYBACK_LEVELS_AVAILABLE, this.fillLevels)
    this.listenTo(currentPlayback, Events.PLAYBACK_LEVEL_SWITCH_START, this.startLevelSwitch)
    this.listenTo(currentPlayback, Events.PLAYBACK_LEVEL_SWITCH_END, this.stopLevelSwitch)
    this.listenTo(currentPlayback, Events.PLAYBACK_BITRATE, this.updateCurrentLevel)

    let playbackLevelsAvaialbeWasTriggered = currentPlayback.levels && currentPlayback.levels.length > 0
    playbackLevelsAvaialbeWasTriggered && this.fillLevels(currentPlayback.levels)
  }

  reload() {
    this.unBindEvents()
    this.bindEvents()
    this.bindPlaybackEvents()
  }

  shouldRender() {
    if (!this.core.activeContainer) return false

    let currentPlayback = this.core.activePlayback
    if (!currentPlayback) return false

    let respondsToCurrentLevel = currentPlayback.currentLevel !== undefined
    // Only care if we have at least 2 to choose from
    let hasLevels = !!(this.levels && this.levels.length > 1)

    return respondsToCurrentLevel && hasLevels
  }

  render() {
    if (this.shouldRender()) {
      let style = Styler.getStyleFor(pluginStyle, { baseUrl: this.core.options.baseUrl })

      this.$el.html(this.template({ 'levels':this.levels, 'title': this.getTitle() }))
      this.$el.append(style)
      this.core.mediaControl.$('.media-control-right-panel').append(this.el)
      this.highlightCurrentLevel()
    }
    return this
  }

  fillLevels(levels, initialLevel = AUTO) {
    if (this.selectedLevelId === undefined) this.selectedLevelId = initialLevel
    this.levels = levels
    this.configureLevelsLabels()
    this.render()
  }

  configureLevelsLabels() {
    if (this.core.options.levelSelectorConfig === undefined) return

    let labelCallback = this.core.options.levelSelectorConfig.labelCallback
    if (labelCallback && typeof labelCallback !== 'function')
      throw new TypeError('labelCallback must be a function')

    let hasLabels = this.core.options.levelSelectorConfig.labels
    let labels = hasLabels ? this.core.options.levelSelectorConfig.labels : {}

    if (labelCallback || hasLabels) {
      let level
      let label
      for (let levelId in this.levels) {
        level = this.levels[levelId]
        label = labels[level.id]
        if (labelCallback)
          level.label = labelCallback(level,label)
        else if (label)
          level.label = label

      }
    }
  }

  findLevelBy(id) {
    let foundLevel
    this.levels.forEach((level) => { if (level.id === id) foundLevel = level })
    return foundLevel
  }

  onLevelSelect(event) {
    this.selectedLevelId = parseInt(event.target.dataset.levelSelectorSelect, 10)
    if (this.core.activePlayback.currentLevel == this.selectedLevelId) return false
    this.core.activePlayback.currentLevel = this.selectedLevelId

    this.toggleContextMenu()

    event.stopPropagation()
    return false
  }

  onShowLevelSelectMenu() { this.toggleContextMenu() }

  hideSelectLevelMenu() { this.$('.level_selector ul').hide() }

  toggleContextMenu() { this.$('.level_selector ul').toggle() }

  buttonElement() { return this.$('.level_selector button') }

  levelElement(id) { return this.$('.level_selector ul a'+(!isNaN(id) ? '[data-level-selector-select="'+id+'"]' : '')).parent() }

  getTitle() { return (this.core.options.levelSelectorConfig || {}).title }

  startLevelSwitch() { this.buttonElement().addClass('changing') }

  stopLevelSwitch() { this.buttonElement().removeClass('changing') }

  updateText(level) {
    if (level === AUTO)
      this.buttonElement().text(this.currentLevel ? 'AUTO (' + this.currentLevel.label + ')' : 'AUTO')

    else
      this.buttonElement().text(this.findLevelBy(level).label)

  }
  updateCurrentLevel(info) {
    let level = this.findLevelBy(info.level)
    this.currentLevel = level ? level : null
    this.highlightCurrentLevel()
  }
  highlightCurrentLevel() {
    this.levelElement().removeClass('current')
    this.currentLevel && this.levelElement(this.currentLevel.id).addClass('current')
    this.updateText(this.selectedLevelId)
  }
}
<commit_msg>fix: add backward compatibility with Clappr 0.2.x<commit_after>import { Events, Styler, UICorePlugin, template } from 'clappr'
import pluginHtml from './public/level-selector.html'
import pluginStyle from './public/style.scss'

const AUTO = -1

export default class LevelSelector extends UICorePlugin {

  static get version() { return VERSION }

  get name() { return 'level_selector' }
  get template() { return template(pluginHtml) }

  get attributes() {
    return {
      'class': this.name,
      'data-level-selector': ''
    }
  }

  get events() {
    return {
      'click [data-level-selector-select]': 'onLevelSelect',
      'click [data-level-selector-button]': 'onShowLevelSelectMenu'
    }
  }

  get __container() {
    return this.core.activeContainer
      ? this.core.activeContainer
      : this.core.mediaControl.container
  }

  get __playback() {
    return this.core.activePlayback
      ? this.core.activePlayback
      : this.core.getCurrentPlayback()
  }

  bindEvents() {
    this.listenTo(this.core, Events.CORE_READY, this.bindPlaybackEvents)
    if (Events.CORE_ACTIVE_CONTAINER_CHANGED)
      this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.reload)
    else
      this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, this.reload)
    this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this.render)
    this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu)
  }

  unBindEvents() {
    this.stopListening(this.core, Events.CORE_READY)
    if (Events.CORE_ACTIVE_CONTAINER_CHANGED)
      this.stopListening(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED)
    else
      this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED)
    this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED)
    this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE)
    this.stopListening(this.__playback, Events.PLAYBACK_LEVELS_AVAILABLE)
    this.stopListening(this.__playback, Events.PLAYBACK_LEVEL_SWITCH_START)
    this.stopListening(this.__playback, Events.PLAYBACK_LEVEL_SWITCH_END)
    this.stopListening(this.__playback, Events.PLAYBACK_BITRATE)
  }

  bindPlaybackEvents() {
    this.listenTo(this.__playback, Events.PLAYBACK_LEVELS_AVAILABLE, this.fillLevels)
    this.listenTo(this.__playback, Events.PLAYBACK_LEVEL_SWITCH_START, this.startLevelSwitch)
    this.listenTo(this.__playback, Events.PLAYBACK_LEVEL_SWITCH_END, this.stopLevelSwitch)
    this.listenTo(this.__playback, Events.PLAYBACK_BITRATE, this.updateCurrentLevel)

    let playbackLevelsAvaialbeWasTriggered = this.__playback.levels && this.__playback.levels.length > 0
    playbackLevelsAvaialbeWasTriggered && this.fillLevels(this.__playback.levels)
  }

  reload() {
    this.unBindEvents()
    this.bindEvents()
    this.bindPlaybackEvents()
  }

  shouldRender() {
    if (!this.__container) return false

    if (!this.__playback) return false

    let respondsToCurrentLevel = this.__playback.currentLevel !== undefined
    // Only care if we have at least 2 to choose from
    let hasLevels = !!(this.levels && this.levels.length > 1)

    return respondsToCurrentLevel && hasLevels
  }

  render() {
    if (this.shouldRender()) {
      let style = Styler.getStyleFor(pluginStyle, { baseUrl: this.core.options.baseUrl })

      this.$el.html(this.template({ 'levels':this.levels, 'title': this.getTitle() }))
      this.$el.append(style)
      this.core.mediaControl.$('.media-control-right-panel').append(this.el)
      this.highlightCurrentLevel()
    }
    return this
  }

  fillLevels(levels, initialLevel = AUTO) {
    if (this.selectedLevelId === undefined) this.selectedLevelId = initialLevel
    this.levels = levels
    this.configureLevelsLabels()
    this.render()
  }

  configureLevelsLabels() {
    if (this.core.options.levelSelectorConfig === undefined) return

    let labelCallback = this.core.options.levelSelectorConfig.labelCallback
    if (labelCallback && typeof labelCallback !== 'function')
      throw new TypeError('labelCallback must be a function')

    let hasLabels = this.core.options.levelSelectorConfig.labels
    let labels = hasLabels ? this.core.options.levelSelectorConfig.labels : {}

    if (labelCallback || hasLabels) {
      let level
      let label
      for (let levelId in this.levels) {
        level = this.levels[levelId]
        label = labels[level.id]
        if (labelCallback)
          level.label = labelCallback(level,label)
        else if (label)
          level.label = label

      }
    }
  }

  findLevelBy(id) {
    let foundLevel
    this.levels.forEach((level) => { if (level.id === id) foundLevel = level })
    return foundLevel
  }

  onLevelSelect(event) {
    this.selectedLevelId = parseInt(event.target.dataset.levelSelectorSelect, 10)
    if (this.__playback.currentLevel == this.selectedLevelId) return false
    this.__playback.currentLevel = this.selectedLevelId

    this.toggleContextMenu()

    event.stopPropagation()
    return false
  }

  onShowLevelSelectMenu() { this.toggleContextMenu() }

  hideSelectLevelMenu() { this.$('.level_selector ul').hide() }

  toggleContextMenu() { this.$('.level_selector ul').toggle() }

  buttonElement() { return this.$('.level_selector button') }

  levelElement(id) { return this.$('.level_selector ul a'+(!isNaN(id) ? '[data-level-selector-select="'+id+'"]' : '')).parent() }

  getTitle() { return (this.core.options.levelSelectorConfig || {}).title }

  startLevelSwitch() { this.buttonElement().addClass('changing') }

  stopLevelSwitch() { this.buttonElement().removeClass('changing') }

  updateText(level) {
    if (level === AUTO)
      this.buttonElement().text(this.currentLevel ? 'AUTO (' + this.currentLevel.label + ')' : 'AUTO')

    else
      this.buttonElement().text(this.findLevelBy(level).label)

  }
  updateCurrentLevel(info) {
    let level = this.findLevelBy(info.level)
    this.currentLevel = level ? level : null
    this.highlightCurrentLevel()
  }
  highlightCurrentLevel() {
    this.levelElement().removeClass('current')
    this.currentLevel && this.levelElement(this.currentLevel.id).addClass('current')
    this.updateText(this.selectedLevelId)
  }
}
<|endoftext|>


<commit_before>// a key map of allowed keys
var allowedKeys = {
  37: 'left',
  38: 'up',
  39: 'right',
  40: 'down',
  65: 'a',
  66: 'b'
};

// the 'official' Konami Code sequence
var konamiCode = ['up', 'up', 'down', 'down', 'left', 'right', 'left', 'right', 'b', 'a'];

// a variable to remember the 'position' the user has reached so far.
var konamiCodePosition = 0;

// add keydown event listener
document.addEventListener('keydown', function(e) {
  // get the value of the key code from the key map
  var key = allowedKeys[e.keyCode];
  // get the value of the required key from the konami code
  var requiredKey = konamiCode[konamiCodePosition];

  // compare the key with the required key
  if (key == requiredKey) {

    // move to the next key in the konami code sequence
    konamiCodePosition++;

    // if the last key is reached, activate cheats
    if (konamiCodePosition == konamiCode.length)
      activateCheats();
  } else
    konamiCodePosition = 0;
});

function activateCheats() {
  chili.electricBugaloo {
    top: 100%;
    left: 100%;
    -webkit-transform: rotate(2000deg);
    transform: rotate(2000deg);

  chili {
    position: fixed;
    width: 32px;
    height: 32px;
    background: url('http://i.imgur.com/SyCqOhA.png');
    top: -5%;
    left: -5%;
    z-index: 11200000;
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
    -webkit-transition: all 2s linear;
    transition: all 2s linear;

  var audio = new Audio('audio/pling.mp3');
  audio.play();

  alert("cheats activated");
}
<commit_msg>Update konami.js<commit_after>// a key map of allowed keys
var allowedKeys = {
  37: 'left',
  38: 'up',
  39: 'right',
  40: 'down',
  65: 'a',
  66: 'b'
};

// the 'official' Konami Code sequence
var konamiCode = ['up', 'up', 'down', 'down', 'left', 'right', 'left', 'right', 'b', 'a'];

// a variable to remember the 'position' the user has reached so far.
var konamiCodePosition = 0;

// add keydown event listener
document.addEventListener('keydown', function(e) {
  // get the value of the key code from the key map
  var key = allowedKeys[e.keyCode];
  // get the value of the required key from the konami code
  var requiredKey = konamiCode[konamiCodePosition];

  // compare the key with the required key
  if (key == requiredKey) {

    // move to the next key in the konami code sequence
    konamiCodePosition++;

    // if the last key is reached, activate cheats
    if (konamiCodePosition == konamiCode.length)
      activateCheats();
  } else
    konamiCodePosition = 0;
});

function activateCheats() {
  chili.electricBugaloo {
    top: 100%;
    left: 100%;
    -webkit-transform: rotate(2000deg);
    transform: rotate(2000deg);
  };

  chili {
    position: fixed;
    width: 32px;
    height: 32px;
    background: url('http://i.imgur.com/SyCqOhA.png');
    top: -5%;
    left: -5%;
    z-index: 11200000;
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
    -webkit-transition: all 2s linear;
    transition: all 2s linear;
  };

  var audio = new Audio('audio/pling.mp3');
  audio.play();

  alert("cheats activated");
}
<|endoftext|>


<commit_before>/**
 This Source Code is licensed under the MIT license. If a copy of the
 MIT-license was not distributed with this file, You can obtain one at:
 http://opensource.org/licenses/mit-license.html.
 @author: Manuel Overdijk (manueloverdijk)
 @license MIT
 @copyright Manuel Overdijk, 2015
 */
"use strict"

var MyoProtocol = require('./myoProtocol');
var noble = require('noble');
var Armband = require('./armband/armband');
var EventEmitter = require("events").EventEmitter;

class Agent extends EventEmitter{

    get armbands(){return this._armbands}
    set armbands(value){this._armbands = value}

    constructor(){
        this.myoProtocol = new MyoProtocol();
        this._armbands = [];
        this.startDiscover();
    }

    /**
     * startDiscover
     * Start discovering MYO peripherals
     */

    startDiscover(){
        noble.on('stateChange', function(state){
            this.emit('stateChange', state);

            /* Start discovering Myo peripherals */
            if (state === 'poweredOn') {
                this.discover(this.myoProtocol.services.control.id);
            } else {

                /* Set all armbands to not connected */
                for(let armband of this.armbands){
                    armband.setConnected(false);
                }
                throw new Error('Bluetooth Adapter not found');

            }
        }.bind(this));
    }

    /**
     * Discovers peripherals which advertise given UUID
     * @param UUID
     */
    discover(UUID){
        console.log('start scanning for MYO devices');

        noble.startScanning([UUID],false);

        noble.on('discover', function (peripheral) {
            let armband = new Armband(peripheral);
            this._armbands.push(armband);
            this.emit('discovered', armband);
        }.bind(this));
    }

    /**
     * stopScanning
     */
    stopScanning(){
        noble.stopScanning();
    }
}

module.exports = Agent;<commit_msg>Added super constructor<commit_after>/**
 This Source Code is licensed under the MIT license. If a copy of the
 MIT-license was not distributed with this file, You can obtain one at:
 http://opensource.org/licenses/mit-license.html.
 @author: Manuel Overdijk (manueloverdijk)
 @license MIT
 @copyright Manuel Overdijk, 2015
 */
"use strict"

var MyoProtocol = require('./myoProtocol');
var noble = require('noble');
var Armband = require('./armband/armband');
var EventEmitter = require("events").EventEmitter;

class Agent extends EventEmitter{

    get armbands(){return this._armbands}
    set armbands(value){this._armbands = value}

    constructor(){
        super();
        this.myoProtocol = new MyoProtocol();
        this._armbands = [];
        this.startDiscover();
    }

    /**
     * startDiscover
     * Start discovering MYO peripherals
     */

    startDiscover(){
        noble.on('stateChange', function(state){
            this.emit('stateChange', state);

            /* Start discovering Myo peripherals */
            if (state === 'poweredOn') {
                this.discover(this.myoProtocol.services.control.id);
            } else {

                /* Set all armbands to not connected */
                for(let armband of this.armbands){
                    armband.setConnected(false);
                }
                throw new Error('Bluetooth Adapter not found');

            }
        }.bind(this));
    }

    /**
     * Discovers peripherals which advertise given UUID
     * @param UUID
     */
    discover(UUID){
        console.log('start scanning for MYO devices');

        noble.startScanning([UUID],false);

        noble.on('discover', function (peripheral) {
            let armband = new Armband(peripheral);
            this._armbands.push(armband);
            this.emit('discovered', armband);
        }.bind(this));
    }

    /**
     * stopScanning
     */
    stopScanning(){
        noble.stopScanning();
    }
}

module.exports = Agent;
<|endoftext|>