<commit_before>'use strict';

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

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

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

module.exports = class itbit extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'itbit',
            'name': 'itBit',
            'countries': [ 'US' ],
            'rateLimit': 2000,
            'version': 'v1',
            'has': {
                'CORS': true,
                'createMarketOrder': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27822159-66153620-60ad-11e7-89e7-005f6d7f3de0.jpg',
                'api': 'https://api.itbit.com',
                'www': 'https://www.itbit.com',
                'doc': [
                    'https://api.itbit.com/docs',
                    'https://www.itbit.com/api',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'markets/{symbol}/ticker',
                        'markets/{symbol}/order_book',
                        'markets/{symbol}/trades',
                    ],
                },
                'private': {
                    'get': [
                        'wallets',
                        'wallets/{walletId}',
                        'wallets/{walletId}/balances/{currencyCode}',
                        'wallets/{walletId}/funding_history',
                        'wallets/{walletId}/trades',
                        'wallets/{walletId}/orders',
                        'wallets/{walletId}/orders/{id}',
                    ],
                    'post': [
                        'wallet_transfers',
                        'wallets',
                        'wallets/{walletId}/cryptocurrency_deposits',
                        'wallets/{walletId}/cryptocurrency_withdrawals',
                        'wallets/{walletId}/orders',
                        'wire_withdrawal',
                    ],
                    'delete': [
                        'wallets/{walletId}/orders/{id}',
                    ],
                },
            },
            'markets': {
                'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD' },
                'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD' },
                'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR' },
            },
            'fees': {
                'trading': {
                    'maker': 0,
                    'taker': 0.2 / 100,
                },
            },
            'commonCurrencies': { // gets extended/overwritten in subclasses
                'XBT': 'BTC',
                'USD': 'USD',
                'SGD': 'SGD',
                'EUR': 'EUR',
                'ETH': 'ETH',
            },
        });
    }

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

    async fetchTicker (symbol, params = {}) {
        let ticker = await this.publicGetMarketsSymbolTicker (this.extend ({
            'symbol': this.marketId (symbol),
        }, params));
        let serverTimeUTC = this.safeString (ticker, 'serverTimeUTC');
        if (!serverTimeUTC)
            throw new ExchangeError (this.id + ' fetchTicker returned a bad response: ' + this.json (ticker));
        let timestamp = this.parse8601 (serverTimeUTC);
        let vwap = this.safeFloat (ticker, 'vwap24h');
        let baseVolume = this.safeFloat (ticker, 'volume24h');
        let quoteVolume = undefined;
        if (baseVolume !== undefined && vwap !== undefined)
            quoteVolume = baseVolume * vwap;
        let last = this.safeFloat (ticker, 'lastPrice');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high24h'),
            'low': this.safeFloat (ticker, 'low24h'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': vwap,
            'open': this.safeFloat (ticker, 'openToday'),
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        };
    }

    parseTrade (trade, market) {
        let timestamp = this.parse8601 (trade['timestamp']);
        if ('matchNumber' in trade) {
            // then this is a public request response, ie, market data
            let id = trade['matchNumber'].toString ();
            return {
                'info': trade,
                'timestamp': timestamp,
                'datetime': this.iso8601 (timestamp),
                'symbol': market['symbol'],
                'id': id,
                'order': id,
                'type': undefined,
                'side': undefined,
                'price': this.safeFloat (trade, 'price'),
                'amount': this.safeFloat (trade, 'amount'),
            };
        } else {
            // this is a private request response, ie, MY trades
            // note on fee from itbit:
            // + **commissionPaid** `string` - *net trade fee paid after using any available rebate balance*
            let fee = {
                'cost': this.safeFloat (trade, 'commissionPaid'),
                'currency': this.safeString (trade, 'commissionCurrency'),
            };
            return {
                'info': trade,
                'timestamp': timestamp,
                'datetime': this.iso8601 (timestamp),
                'symbol': this.marketsById[this.safeString (trade, 'instrument')].symbol,
                'id': this.safeString (trade, 'executionId'),
                'order': this.safeString (trade, 'orderId'),
                'type': undefined,
                'side': this.safeString (trade, 'direction'),
                'price': this.safeFloat (trade, 'rate'),
                'amount': this.safeFloat (trade, 'currency1Amount'),
                'fee': fee,
            };
        }
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        const walletId = this.safeString (params, 'walletId');
        if (walletId === undefined) {
            throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter');
        }
        await this.loadMarkets ();
        const request = {
            'walletId': walletId,
        };
        if (since !== undefined) {
            request['rangeStart'] = this.ymdhms (since, 'T');
        }
        if (limit !== undefined) {
            request['perPage'] = limit; // default 50, max 50
        }
        const response = await this.privateGetWalletsWalletIdTrades (this.extend (request, params));
        const trades = this.safeValue (response, 'tradingHistory', []);
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market (symbol);
        }
        return this.parseTrades (trades, market, since, limit);
    }

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

    async fetchBalance (params = {}) {
        const response = await this.fetchWallets (params);
        const balances = response[0]['balances'];
        const result = { 'info': response };
        for (let i = 0; i < balances.length; i++) {
            const balance = balances[i];
            const currencyId = this.safeString (balance, 'currency');
            const code = this.commonCurrencyCode (currencyId);
            let account = {
                'free': this.safeFloat (balance, 'availableBalance'),
                'used': 0.0,
                'total': this.safeFloat (balance, 'totalBalance'),
            };
            account['used'] = account['total'] - account['free'];
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchWallets (params = {}) {
        if (!this.uid)
            throw new AuthenticationError (this.id + ' fetchWallets requires uid API credential');
        let request = {
            'userId': this.uid,
        };
        return this.privateGetWallets (this.extend (request, params));
    }

    async fetchWallet (walletId, params = {}) {
        let wallet = {
            'walletId': walletId,
        };
        return this.privateGetWalletsWalletId (this.extend (wallet, params));
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrders (symbol, since, limit, this.extend ({
            'status': 'open',
        }, params));
    }

    async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrders (symbol, since, limit, this.extend ({
            'status': 'filled',
        }, params));
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        let walletIdInParams = ('walletId' in params);
        if (!walletIdInParams)
            throw new ExchangeError (this.id + ' fetchOrders requires a walletId parameter');
        let walletId = params['walletId'];
        let response = await this.privateGetWalletsWalletIdOrders (this.extend ({
            'walletId': walletId,
        }, params));
        let orders = this.parseOrders (response, undefined, since, limit);
        return orders;
    }

    parseOrder (order, market = undefined) {
        let side = order['side'];
        let type = order['type'];
        let symbol = this.markets_by_id[order['instrument']]['symbol'];
        let timestamp = this.parse8601 (order['createdTime']);
        let amount = this.safeFloat (order, 'amount');
        let filled = this.safeFloat (order, 'amountFilled');
        let remaining = amount - filled;
        let fee = undefined;
        let price = this.safeFloat (order, 'price');
        let cost = price * this.safeFloat (order, 'volumeWeightedAveragePrice');
        return {
            'id': order['id'],
            'info': order,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'status': order['status'],
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'fee': fee,
            // 'trades': this.parseTrades (order['trades'], market),
        };
    }

    nonce () {
        return this.milliseconds ();
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market')
            throw new ExchangeError (this.id + ' allows limit orders only');
        let walletIdInParams = ('walletId' in params);
        if (!walletIdInParams)
            throw new ExchangeError (this.id + ' createOrder requires a walletId parameter');
        amount = amount.toString ();
        price = price.toString ();
        let market = this.market (symbol);
        let order = {
            'side': side,
            'type': type,
            'currency': market['id'].replace (market['quote'], ''),
            'amount': amount,
            'display': amount,
            'price': price,
            'instrument': market['id'],
        };
        let response = await this.privatePostWalletsWalletIdOrders (this.extend (order, params));
        return {
            'info': response,
            'id': response['id'],
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        const walletIdInParams = ('walletId' in params);
        if (!walletIdInParams) {
            throw new ExchangeError (this.id + ' fetchOrder requires a walletId parameter');
        }
        const request = { 'id': id };
        const response = await this.privateGetWalletsWalletIdOrdersId (this.extend (request, params));
        return this.parseOrder (response);
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        let walletIdInParams = ('walletId' in params);
        if (!walletIdInParams)
            throw new ExchangeError (this.id + ' cancelOrder requires a walletId parameter');
        return await this.privateDeleteWalletsWalletIdOrdersId (this.extend ({
            'id': id,
        }, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (method === 'GET' && Object.keys (query).length)
            url += '?' + this.urlencode (query);
        if (method === 'POST' && Object.keys (query).length)
            body = this.json (query);
        else
            body = '';
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let timestamp = nonce;
            let auth = [ method, url, body, nonce, timestamp ];
            let message = nonce + this.json (auth).replace ('\\/', '/');
            let hash = this.hash (this.encode (message), 'sha256', 'binary');
            let binaryUrl = this.stringToBinary (this.encode (url));
            let binhash = this.binaryConcat (binaryUrl, hash);
            let signature = this.hmac (binhash, this.encode (this.secret), 'sha512', 'base64');
            headers = {
                'Authorization': this.apiKey + ':' + signature,
                'Content-Type': 'application/json',
                'X-Auth-Timestamp': timestamp,
                'X-Auth-Nonce': nonce,
            };
        }
        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 ('code' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<commit_msg>itbit linting<commit_after>'use strict';

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

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

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

module.exports = class itbit extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'itbit',
            'name': 'itBit',
            'countries': [ 'US' ],
            'rateLimit': 2000,
            'version': 'v1',
            'has': {
                'CORS': true,
                'createMarketOrder': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27822159-66153620-60ad-11e7-89e7-005f6d7f3de0.jpg',
                'api': 'https://api.itbit.com',
                'www': 'https://www.itbit.com',
                'doc': [
                    'https://api.itbit.com/docs',
                    'https://www.itbit.com/api',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'markets/{symbol}/ticker',
                        'markets/{symbol}/order_book',
                        'markets/{symbol}/trades',
                    ],
                },
                'private': {
                    'get': [
                        'wallets',
                        'wallets/{walletId}',
                        'wallets/{walletId}/balances/{currencyCode}',
                        'wallets/{walletId}/funding_history',
                        'wallets/{walletId}/trades',
                        'wallets/{walletId}/orders',
                        'wallets/{walletId}/orders/{id}',
                    ],
                    'post': [
                        'wallet_transfers',
                        'wallets',
                        'wallets/{walletId}/cryptocurrency_deposits',
                        'wallets/{walletId}/cryptocurrency_withdrawals',
                        'wallets/{walletId}/orders',
                        'wire_withdrawal',
                    ],
                    'delete': [
                        'wallets/{walletId}/orders/{id}',
                    ],
                },
            },
            'markets': {
                'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD' },
                'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD' },
                'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR' },
            },
            'fees': {
                'trading': {
                    'maker': 0,
                    'taker': 0.2 / 100,
                },
            },
            'commonCurrencies': {
                'XBT': 'BTC',
                'USD': 'USD',
                'SGD': 'SGD',
                'EUR': 'EUR',
                'ETH': 'ETH',
            },
        });
    }

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

    async fetchTicker (symbol, params = {}) {
        let ticker = await this.publicGetMarketsSymbolTicker (this.extend ({
            'symbol': this.marketId (symbol),
        }, params));
        let serverTimeUTC = this.safeString (ticker, 'serverTimeUTC');
        if (!serverTimeUTC)
            throw new ExchangeError (this.id + ' fetchTicker returned a bad response: ' + this.json (ticker));
        let timestamp = this.parse8601 (serverTimeUTC);
        let vwap = this.safeFloat (ticker, 'vwap24h');
        let baseVolume = this.safeFloat (ticker, 'volume24h');
        let quoteVolume = undefined;
        if (baseVolume !== undefined && vwap !== undefined)
            quoteVolume = baseVolume * vwap;
        let last = this.safeFloat (ticker, 'lastPrice');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high24h'),
            'low': this.safeFloat (ticker, 'low24h'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': vwap,
            'open': this.safeFloat (ticker, 'openToday'),
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        };
    }

    parseTrade (trade, market) {
        let timestamp = this.parse8601 (trade['timestamp']);
        if ('matchNumber' in trade) {
            // then this is a public request response, ie, market data
            let id = trade['matchNumber'].toString ();
            return {
                'info': trade,
                'timestamp': timestamp,
                'datetime': this.iso8601 (timestamp),
                'symbol': market['symbol'],
                'id': id,
                'order': id,
                'type': undefined,
                'side': undefined,
                'price': this.safeFloat (trade, 'price'),
                'amount': this.safeFloat (trade, 'amount'),
            };
        } else {
            // this is a private request response, ie, MY trades
            // note on fee from itbit:
            // + **commissionPaid** `string` - *net trade fee paid after using any available rebate balance*
            let fee = {
                'cost': this.safeFloat (trade, 'commissionPaid'),
                'currency': this.safeString (trade, 'commissionCurrency'),
            };
            return {
                'info': trade,
                'timestamp': timestamp,
                'datetime': this.iso8601 (timestamp),
                'symbol': this.marketsById[this.safeString (trade, 'instrument')].symbol,
                'id': this.safeString (trade, 'executionId'),
                'order': this.safeString (trade, 'orderId'),
                'type': undefined,
                'side': this.safeString (trade, 'direction'),
                'price': this.safeFloat (trade, 'rate'),
                'amount': this.safeFloat (trade, 'currency1Amount'),
                'fee': fee,
            };
        }
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        const walletId = this.safeString (params, 'walletId');
        if (walletId === undefined) {
            throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter');
        }
        await this.loadMarkets ();
        const request = {
            'walletId': walletId,
        };
        if (since !== undefined) {
            request['rangeStart'] = this.ymdhms (since, 'T');
        }
        if (limit !== undefined) {
            request['perPage'] = limit; // default 50, max 50
        }
        const response = await this.privateGetWalletsWalletIdTrades (this.extend (request, params));
        const trades = this.safeValue (response, 'tradingHistory', []);
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market (symbol);
        }
        return this.parseTrades (trades, market, since, limit);
    }

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

    async fetchBalance (params = {}) {
        const response = await this.fetchWallets (params);
        const balances = response[0]['balances'];
        const result = { 'info': response };
        for (let i = 0; i < balances.length; i++) {
            const balance = balances[i];
            const currencyId = this.safeString (balance, 'currency');
            const code = this.commonCurrencyCode (currencyId);
            let account = {
                'free': this.safeFloat (balance, 'availableBalance'),
                'used': 0.0,
                'total': this.safeFloat (balance, 'totalBalance'),
            };
            account['used'] = account['total'] - account['free'];
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchWallets (params = {}) {
        if (!this.uid)
            throw new AuthenticationError (this.id + ' fetchWallets requires uid API credential');
        let request = {
            'userId': this.uid,
        };
        return this.privateGetWallets (this.extend (request, params));
    }

    async fetchWallet (walletId, params = {}) {
        let wallet = {
            'walletId': walletId,
        };
        return this.privateGetWalletsWalletId (this.extend (wallet, params));
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrders (symbol, since, limit, this.extend ({
            'status': 'open',
        }, params));
    }

    async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrders (symbol, since, limit, this.extend ({
            'status': 'filled',
        }, params));
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        let walletIdInParams = ('walletId' in params);
        if (!walletIdInParams)
            throw new ExchangeError (this.id + ' fetchOrders requires a walletId parameter');
        let walletId = params['walletId'];
        let response = await this.privateGetWalletsWalletIdOrders (this.extend ({
            'walletId': walletId,
        }, params));
        let orders = this.parseOrders (response, undefined, since, limit);
        return orders;
    }

    parseOrder (order, market = undefined) {
        let side = order['side'];
        let type = order['type'];
        let symbol = this.markets_by_id[order['instrument']]['symbol'];
        let timestamp = this.parse8601 (order['createdTime']);
        let amount = this.safeFloat (order, 'amount');
        let filled = this.safeFloat (order, 'amountFilled');
        let remaining = amount - filled;
        let fee = undefined;
        let price = this.safeFloat (order, 'price');
        let cost = price * this.safeFloat (order, 'volumeWeightedAveragePrice');
        return {
            'id': order['id'],
            'info': order,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'status': order['status'],
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'fee': fee,
            // 'trades': this.parseTrades (order['trades'], market),
        };
    }

    nonce () {
        return this.milliseconds ();
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market')
            throw new ExchangeError (this.id + ' allows limit orders only');
        let walletIdInParams = ('walletId' in params);
        if (!walletIdInParams)
            throw new ExchangeError (this.id + ' createOrder requires a walletId parameter');
        amount = amount.toString ();
        price = price.toString ();
        let market = this.market (symbol);
        let order = {
            'side': side,
            'type': type,
            'currency': market['id'].replace (market['quote'], ''),
            'amount': amount,
            'display': amount,
            'price': price,
            'instrument': market['id'],
        };
        let response = await this.privatePostWalletsWalletIdOrders (this.extend (order, params));
        return {
            'info': response,
            'id': response['id'],
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        const walletIdInParams = ('walletId' in params);
        if (!walletIdInParams) {
            throw new ExchangeError (this.id + ' fetchOrder requires a walletId parameter');
        }
        const request = { 'id': id };
        const response = await this.privateGetWalletsWalletIdOrdersId (this.extend (request, params));
        return this.parseOrder (response);
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        let walletIdInParams = ('walletId' in params);
        if (!walletIdInParams)
            throw new ExchangeError (this.id + ' cancelOrder requires a walletId parameter');
        return await this.privateDeleteWalletsWalletIdOrdersId (this.extend ({
            'id': id,
        }, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (method === 'GET' && Object.keys (query).length)
            url += '?' + this.urlencode (query);
        if (method === 'POST' && Object.keys (query).length)
            body = this.json (query);
        else
            body = '';
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let timestamp = nonce;
            let auth = [ method, url, body, nonce, timestamp ];
            let message = nonce + this.json (auth).replace ('\\/', '/');
            let hash = this.hash (this.encode (message), 'sha256', 'binary');
            let binaryUrl = this.stringToBinary (this.encode (url));
            let binhash = this.binaryConcat (binaryUrl, hash);
            let signature = this.hmac (binhash, this.encode (this.secret), 'sha512', 'base64');
            headers = {
                'Authorization': this.apiKey + ':' + signature,
                'Content-Type': 'application/json',
                'X-Auth-Timestamp': timestamp,
                'X-Auth-Nonce': nonce,
            };
        }
        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 ('code' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<|endoftext|>


<commit_before>'use strict';

const Script = require('smooch-bot').Script;

module.exports = new Script({
    processing: {
        prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hallo! ich bin Andreas Sefzigs Bot.')
                .then(() => bot.say('Wenn Andreas online ist, sieht er diese Unterhaltung.'))
                .then(() => 'askName');
        }
    },

    askName: {
        prompt: (bot) => bot.say('Wie sollen wir Sie nennen?'),
        receive: (bot, message) => {
            const name = message.text;
            return bot.setProp('name', name)
                .then(() => bot.say(`Prima, wir nennen Sie ${name}.`)),
                .then(() => bot.say('Wollen Sie --Person oder --Kompetenzen?')),
                .then(() => 'register');
        }
    },

    register: {
        receive: (bot, message) => {

            const wollen = message.text;
            var befehl = wollen;
            befehl = befehl.trim();
            befehl = befehl.toUpperCase();

            if (befehl == "--HALLO")     { bot.say('Begrussung und alles.'); }
            if (befehl == "--ABBRECHEN") { bot.say('Text zum Abbruch.'); }

            return bot.setProp('wollen', wollen)
                .then(() => 'register');
        }
    },

    finish: {
        receive: (bot, message) => {
            return bot.getProp('name')
                .then(() => 'finish');
        }
    }
});
<commit_msg>Tests<commit_after>'use strict';

const Script = require('smooch-bot').Script;

module.exports = new Script({
    processing: {
        prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hallo! ich bin Andreas Sefzigs Bot.')
                .then(() => bot.say('Wenn Andreas online ist, sieht er diese Unterhaltung.'))
                .then(() => 'askName');
        }
    },

    askName: {
        prompt: (bot) => bot.say('Wie sollen wir Sie nennen?'),
        receive: (bot, message) => {
            const name = message.text;
            return bot.setProp('name', name)
                .then(() => bot.say(`Prima, wir nennen Sie ${name}.`))
                .then(() => bot.say('Wollen Sie --Person oder --Kompetenzen?'))
                .then(() => 'register');
        }
    },

    register: {
        receive: (bot, message) => {

            const wollen = message.text;
            var befehl = wollen;
            befehl = befehl.trim();
            befehl = befehl.toUpperCase();

            if (befehl == "--HALLO")     { bot.say('Begrussung und alles.'); }
            if (befehl == "--ABBRECHEN") { bot.say('Text zum Abbruch.'); }

            return bot.setProp('wollen', wollen)
                .then(() => 'register');
        }
    },

    finish: {
        receive: (bot, message) => {
            return bot.getProp('name')
                .then(() => 'finish');
        }
    }
});
<|endoftext|>


<commit_before>(function()
{
    'use strict';

    angular
        .module('lumx.dropdown')
        .directive('lxDropdown', lxDropdown)
        .directive('lxDropdownToggle', lxDropdownToggle)
        .directive('lxDropdownMenu', lxDropdownMenu)
        .directive('lxDropdownFilter', lxDropdownFilter);

    lxDropdown.$inject = ['$document'];

    function lxDropdown($document)
    {
        return {
            restrict: 'E',
            templateUrl: 'dropdown.html',
            scope:
            {
                escapeClose: '=?lxEscapeClose',
                overToggle: '=?lxOverToggle',
                position: '@?lxPosition',
                width: '@?lxWidth'
            },
            link: link,
            controller: LxDropdownController,
            controllerAs: 'lxDropdown',
            bindToController: true,
            transclude: true
        };

        function link(scope, element, attrs, ctrl)
        {
            $document.bind('click', closeDropdownMenu);

            scope.$on('$destroy', function()
            {
                ctrl.closeDropdownMenu();
                $document.unbind('click', closeDropdownMenu);
            });

            function closeDropdownMenu()
            {
                scope.$apply(function()
                {
                    ctrl.closeDropdownMenu();
                });
            }
        }
    }

    LxDropdownController.$inject = ['$element', '$scope', '$timeout', '$window', 'LxEventSchedulerService'];

    function LxDropdownController($element, $scope, $timeout, $window, LxEventSchedulerService)
    {
        var lxDropdown = this;
        var dropdownMenu;
        var dropdownToggle;
        var idEventScheduler;

        lxDropdown.closeDropdownMenu = closeDropdownMenu;
        lxDropdown.registerDropdownMenu = registerDropdownMenu;
        lxDropdown.registerDropdownToggle = registerDropdownToggle;
        lxDropdown.toggle = toggle;

        lxDropdown.escapeClose = angular.isDefined(lxDropdown.escapeClose) ? lxDropdown.escapeClose : true;
        lxDropdown.isOpen = false;
        lxDropdown.overToggle = angular.isDefined(lxDropdown.overToggle) ? lxDropdown.overToggle : false;
        lxDropdown.position = angular.isDefined(lxDropdown.position) ? lxDropdown.position : 'left';

        ////////////

        function closeDropdownMenu()
        {
            dropdownMenu.velocity(
            {
                width: 0,
                height: 0,
            },
            {
                duration: 200,
                easing: 'easeOutQuint',
                complete: function()
                {
                    dropdownMenu.removeAttr('style');

                    $scope.$apply(function()
                    {
                        lxDropdown.isOpen = false;
                        $element.removeClass('dropdown--is-open');

                        if (lxDropdown.escapeClose)
                        {
                            LxEventSchedulerService.unregister(idEventScheduler);
                            idEventScheduler = undefined;
                        }
                    });
                }
            });
        }

        function openDropdownMenu()
        {
            lxDropdown.isOpen = true;
            $element.addClass('dropdown--is-open');

            if (lxDropdown.escapeClose)
            {
                idEventScheduler = LxEventSchedulerService.register('keyup', onKeyUp);
            }

            $timeout(function()
            {
                var enoughtHeight = true;
                var availableHeight = setDropdownMenuCss();
                var width = dropdownMenu.outerWidth();
                var height = dropdownMenu.outerHeight();

                if (availableHeight < height)
                {
                    enoughtHeight = false;
                    height = availableHeight;
                }

                dropdownMenu.css(
                {
                    width: 0,
                    height: 0,
                    opacity: 1
                });

                dropdownMenu.find('.dropdown-menu__content').css(
                {
                    width: width,
                    height: height
                });

                dropdownMenu.velocity(
                {
                    width: width
                },
                {
                    duration: 200,
                    easing: 'easeOutQuint',
                    queue: false
                });

                dropdownMenu.velocity(
                {
                    height: height
                },
                {
                    duration: 500,
                    easing: 'easeOutQuint',
                    queue: false,
                    complete: function()
                    {
                        if (enoughtHeight)
                        {
                            dropdownMenu.css(
                            {
                                height: 'auto'
                            });
                        }

                        if (angular.isUndefined(lxDropdown.width))
                        {
                            dropdownMenu.css(
                            {
                                width: 'auto'
                            });
                        }

                        dropdownMenu.find('.dropdown-menu__content').removeAttr('style');
                    }
                });
            });
        }

        function onKeyUp(_event)
        {
            if (_event.keyCode == 27)
            {
                closeDropdownMenu();
            }

            _event.stopPropagation();
        }

        function registerDropdownMenu(_dropdownMenu)
        {
            dropdownMenu = _dropdownMenu;
        }

        function registerDropdownToggle(_dropdownToggle)
        {
            dropdownToggle = _dropdownToggle;
        }

        function setDropdownMenuCss()
        {
            var windowHeight = $window.innerHeight;
            var dropdownToggleHeight = dropdownToggle.outerHeight();
            var dropdownToggleTop = dropdownToggle.offset().top - angular.element($window).scrollTop();
            var dropdownMenuTopAvailable;
            var dropdownMenuBottomAvailable;

            if (lxDropdown.overToggle)
            {
                dropdownMenuTopAvailable = dropdownToggleTop + dropdownToggleHeight;
                dropdownMenuBottomAvailable = windowHeight - dropdownToggleTop;
            }
            else
            {
                dropdownMenuTopAvailable = dropdownToggleTop;
                dropdownMenuBottomAvailable = windowHeight - (dropdownToggleTop + dropdownToggleHeight);
            }

            dropdownMenu.css(
            {
                right: lxDropdown.position === 'right' ? 0 : 'auto',
                left: lxDropdown.position === 'right' ? 'auto' : 0,
                width: angular.isDefined(lxDropdown.width) ? lxDropdown.width : 'auto'
            });

            if (dropdownMenuTopAvailable > dropdownMenuBottomAvailable)
            {
                dropdownMenu.css(
                {
                    bottom: lxDropdown.overToggle ? 0 : dropdownToggleHeight
                });

                return dropdownMenuTopAvailable;
            }
            else
            {
                if (lxDropdown.overToggle)
                {
                    dropdownMenu.css(
                    {
                        top: 0
                    });
                }

                return dropdownMenuBottomAvailable;
            }
        }

        function toggle()
        {
            if (!lxDropdown.isOpen)
            {
                openDropdownMenu();
            }
            else
            {
                closeDropdownMenu();
            }
        }
    }

    function lxDropdownToggle()
    {
        return {
            restrict: 'AE',
            templateUrl: 'dropdown.html',
            require: '^lxDropdown',
            scope: true,
            link: link,
            replace: true,
            transclude: true
        };

        function link(scope, element, attrs, ctrl)
        {
            ctrl.registerDropdownToggle(element);

            element.bind('click', function(_event)
            {
                _event.stopPropagation();

                angular.element('.dropdown').each(function(index, dropdownElem)
                {
                    if (angular.isDefined(angular.element(dropdownElem).scope().lxDropdown) && angular.element(dropdownElem).scope().lxDropdown.isOpen)
                    {
                        angular.element(dropdownElem).scope().lxDropdown.closeDropdownMenu();
                    }
                });

                scope.$apply(function()
                {
                    ctrl.toggle();
                });
            });
        }
    }

    function lxDropdownMenu()
    {
        return {
            restrict: 'E',
            templateUrl: 'dropdown-menu.html',
            require: ['lxDropdownMenu', '^lxDropdown'],
            scope: true,
            link: link,
            controller: LxDropdownMenuController,
            controllerAs: 'lxDropdownMenu',
            bindToController: true,
            replace: true,
            transclude: true
        };

        function link(scope, element, attrs, ctrls)
        {
            ctrls[1].registerDropdownMenu(element);
            ctrls[0].setParentController(ctrls[1]);
        }
    }

    function LxDropdownMenuController()
    {
        var lxDropdownMenu = this;

        lxDropdownMenu.setParentController = setParentController;

        ////////////

        function setParentController(_parentCtrl)
        {
            lxDropdownMenu.parentCtrl = _parentCtrl;
        }
    }

    lxDropdownFilter.$inject = ['$timeout'];

    function lxDropdownFilter($timeout)
    {
        return {
            restrict: 'A',
            link: link
        };

        function link(scope, element)
        {
            element.bind('click', function(_event)
            {
                _event.stopPropagation();
            });

            $timeout(function()
            {
                element.find('input').focus();
            }, 200);
        }
    }
})();<commit_msg>feat dropdown: ensure backward compatibility<commit_after>(function()
{
    'use strict';

    angular
        .module('lumx.dropdown')
        .directive('lxDropdown', lxDropdown)
        .directive('lxDropdownToggle', lxDropdownToggle)
        .directive('lxDropdownMenu', lxDropdownMenu)
        .directive('lxDropdownFilter', lxDropdownFilter);

    lxDropdown.$inject = ['$document'];

    function lxDropdown($document)
    {
        return {
            restrict: 'E',
            templateUrl: 'dropdown.html',
            scope:
            {
                escapeClose: '=?lxEscapeClose',
                overToggle: '=?lxOverToggle',
                position: '@?lxPosition',
                width: '@?lxWidth'
            },
            link: link,
            controller: LxDropdownController,
            controllerAs: 'lxDropdown',
            bindToController: true,
            transclude: true
        };

        function link(scope, element, attrs, ctrl)
        {
            var backwardOneWay = ['position', 'width'];
            var backwardTwoWay = ['escapeClose', 'overToggle'];

            angular.forEach(backwardOneWay, function(attribute)
            {
                if (angular.isDefined(attrs[attribute]))
                {
                    attrs.$observe(attribute, function(newValue)
                    {
                        scope.lxTextField[attribute] = newValue;
                    });
                }
            });

            angular.forEach(backwardTwoWay, function(attribute)
            {
                if (angular.isDefined(attrs[attribute]))
                {
                    scope.$watch(function()
                    {
                        return scope.$parent.$eval(attrs[attribute]);
                    }, function(newValue)
                    {
                        scope.lxTextField[attribute] = newValue;
                    });
                }
            });

            $document.bind('click', closeDropdownMenu);

            scope.$on('$destroy', function()
            {
                ctrl.closeDropdownMenu();
                $document.unbind('click', closeDropdownMenu);
            });

            function closeDropdownMenu()
            {
                scope.$apply(function()
                {
                    ctrl.closeDropdownMenu();
                });
            }
        }
    }

    LxDropdownController.$inject = ['$element', '$scope', '$timeout', '$window', 'LxEventSchedulerService'];

    function LxDropdownController($element, $scope, $timeout, $window, LxEventSchedulerService)
    {
        var lxDropdown = this;
        var dropdownMenu;
        var dropdownToggle;
        var idEventScheduler;

        lxDropdown.closeDropdownMenu = closeDropdownMenu;
        lxDropdown.registerDropdownMenu = registerDropdownMenu;
        lxDropdown.registerDropdownToggle = registerDropdownToggle;
        lxDropdown.toggle = toggle;

        lxDropdown.escapeClose = angular.isDefined(lxDropdown.escapeClose) ? lxDropdown.escapeClose : true;
        lxDropdown.isOpen = false;
        lxDropdown.overToggle = angular.isDefined(lxDropdown.overToggle) ? lxDropdown.overToggle : false;
        lxDropdown.position = angular.isDefined(lxDropdown.position) ? lxDropdown.position : 'left';

        ////////////

        function closeDropdownMenu()
        {
            dropdownMenu.velocity(
            {
                width: 0,
                height: 0,
            },
            {
                duration: 200,
                easing: 'easeOutQuint',
                complete: function()
                {
                    dropdownMenu.removeAttr('style');

                    $scope.$apply(function()
                    {
                        lxDropdown.isOpen = false;
                        $element.removeClass('dropdown--is-open');

                        if (lxDropdown.escapeClose)
                        {
                            LxEventSchedulerService.unregister(idEventScheduler);
                            idEventScheduler = undefined;
                        }
                    });
                }
            });
        }

        function openDropdownMenu()
        {
            lxDropdown.isOpen = true;
            $element.addClass('dropdown--is-open');

            if (lxDropdown.escapeClose)
            {
                idEventScheduler = LxEventSchedulerService.register('keyup', onKeyUp);
            }

            $timeout(function()
            {
                var enoughtHeight = true;
                var availableHeight = setDropdownMenuCss();
                var width = dropdownMenu.outerWidth();
                var height = dropdownMenu.outerHeight();

                if (availableHeight < height)
                {
                    enoughtHeight = false;
                    height = availableHeight;
                }

                dropdownMenu.css(
                {
                    width: 0,
                    height: 0,
                    opacity: 1
                });

                dropdownMenu.find('.dropdown-menu__content').css(
                {
                    width: width,
                    height: height
                });

                dropdownMenu.velocity(
                {
                    width: width
                },
                {
                    duration: 200,
                    easing: 'easeOutQuint',
                    queue: false
                });

                dropdownMenu.velocity(
                {
                    height: height
                },
                {
                    duration: 500,
                    easing: 'easeOutQuint',
                    queue: false,
                    complete: function()
                    {
                        if (enoughtHeight)
                        {
                            dropdownMenu.css(
                            {
                                height: 'auto'
                            });
                        }

                        if (angular.isUndefined(lxDropdown.width))
                        {
                            dropdownMenu.css(
                            {
                                width: 'auto'
                            });
                        }

                        dropdownMenu.find('.dropdown-menu__content').removeAttr('style');
                    }
                });
            });
        }

        function onKeyUp(_event)
        {
            if (_event.keyCode == 27)
            {
                closeDropdownMenu();
            }

            _event.stopPropagation();
        }

        function registerDropdownMenu(_dropdownMenu)
        {
            dropdownMenu = _dropdownMenu;
        }

        function registerDropdownToggle(_dropdownToggle)
        {
            dropdownToggle = _dropdownToggle;
        }

        function setDropdownMenuCss()
        {
            var windowHeight = $window.innerHeight;
            var dropdownToggleHeight = dropdownToggle.outerHeight();
            var dropdownToggleTop = dropdownToggle.offset().top - angular.element($window).scrollTop();
            var dropdownMenuTopAvailable;
            var dropdownMenuBottomAvailable;

            if (lxDropdown.overToggle)
            {
                dropdownMenuTopAvailable = dropdownToggleTop + dropdownToggleHeight;
                dropdownMenuBottomAvailable = windowHeight - dropdownToggleTop;
            }
            else
            {
                dropdownMenuTopAvailable = dropdownToggleTop;
                dropdownMenuBottomAvailable = windowHeight - (dropdownToggleTop + dropdownToggleHeight);
            }

            dropdownMenu.css(
            {
                right: lxDropdown.position === 'right' ? 0 : 'auto',
                left: lxDropdown.position === 'right' ? 'auto' : 0,
                width: angular.isDefined(lxDropdown.width) ? lxDropdown.width : 'auto'
            });

            if (dropdownMenuTopAvailable > dropdownMenuBottomAvailable)
            {
                dropdownMenu.css(
                {
                    bottom: lxDropdown.overToggle ? 0 : dropdownToggleHeight
                });

                return dropdownMenuTopAvailable;
            }
            else
            {
                if (lxDropdown.overToggle)
                {
                    dropdownMenu.css(
                    {
                        top: 0
                    });
                }

                return dropdownMenuBottomAvailable;
            }
        }

        function toggle()
        {
            if (!lxDropdown.isOpen)
            {
                openDropdownMenu();
            }
            else
            {
                closeDropdownMenu();
            }
        }
    }

    function lxDropdownToggle()
    {
        return {
            restrict: 'AE',
            templateUrl: 'dropdown.html',
            require: '^lxDropdown',
            scope: true,
            link: link,
            replace: true,
            transclude: true
        };

        function link(scope, element, attrs, ctrl)
        {
            ctrl.registerDropdownToggle(element);

            element.bind('click', function(_event)
            {
                _event.stopPropagation();

                angular.element('.dropdown').each(function(index, dropdownElem)
                {
                    if (angular.isDefined(angular.element(dropdownElem).scope().lxDropdown) && angular.element(dropdownElem).scope().lxDropdown.isOpen)
                    {
                        angular.element(dropdownElem).scope().lxDropdown.closeDropdownMenu();
                    }
                });

                scope.$apply(function()
                {
                    ctrl.toggle();
                });
            });
        }
    }

    function lxDropdownMenu()
    {
        return {
            restrict: 'E',
            templateUrl: 'dropdown-menu.html',
            require: ['lxDropdownMenu', '^lxDropdown'],
            scope: true,
            link: link,
            controller: LxDropdownMenuController,
            controllerAs: 'lxDropdownMenu',
            bindToController: true,
            replace: true,
            transclude: true
        };

        function link(scope, element, attrs, ctrls)
        {
            ctrls[1].registerDropdownMenu(element);
            ctrls[0].setParentController(ctrls[1]);
        }
    }

    function LxDropdownMenuController()
    {
        var lxDropdownMenu = this;

        lxDropdownMenu.setParentController = setParentController;

        ////////////

        function setParentController(_parentCtrl)
        {
            lxDropdownMenu.parentCtrl = _parentCtrl;
        }
    }

    lxDropdownFilter.$inject = ['$timeout'];

    function lxDropdownFilter($timeout)
    {
        return {
            restrict: 'A',
            link: link
        };

        function link(scope, element)
        {
            element.bind('click', function(_event)
            {
                _event.stopPropagation();
            });

            $timeout(function()
            {
                element.find('input').focus();
            }, 200);
        }
    }
})();<|endoftext|>


<commit_before>/* jshint devel:true*/
/* global define, require */
define(['jquery', 'underscore', 'backbone', 'oro/mediator'],
function($, _, Backbone, mediator) {
    'use strict';

    /**
     * @export  oro/abstract-widget
     * @class   oro.AbstractWidget
     * @extends Backbone.View
     */
    return Backbone.View.extend({
        options: {
            type: 'widget',
            actionsEl: '.widget-actions',
            url: false,
            elementFirst: true,
            title: '',
            alias: null,
            wid: null
        },

        initialize: function(options) {
            options = options || {};
            this.initializeWidget(options);
        },

        setTitle: function(title) {
            console.warn('Implement setTitle');
        },

        getActionsElement: function() {
            console.warn('Implement getActionsElement');
        },

        remove: function() {
            // cause that's circular dependency
            mediator.trigger('widget_remove', this.getWid());
            Backbone.View.prototype.remove.call(this);
        },

        /**
         * Initialize
         */
        initializeWidget: function(options) {
            if (this.options.wid) {
                this._wid = this.options.wid;
            }

            this.on('adoptedFormSubmitClick', _.bind(this._onAdoptedFormSubmitClick, this));
            this.on('adoptedFormResetClick', _.bind(this._onAdoptedFormResetClick, this));
            this.on('adoptedFormSubmit', _.bind(this._onAdoptedFormSubmit, this));

            this.actions = {};
            this.firstRun = true;
        },

        getWid: function() {
            if (!this._wid) {
                this._wid = this._getUniqueIdentifier();
            }
            return this._wid;
        },

        getAlias: function() {
            return this.$el.data('alias') || this.options.alias;
        },

        _getUniqueIdentifier: function() {
            /*jslint bitwise:true */
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0,
                    v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        },

        /**
         * Move form actions to widget actions
         */
        _adoptWidgetActions: function() {
            this.actions['adopted'] = {};
            var adoptedActionsContainer = this._getAdoptedActionsContainer();
            if (adoptedActionsContainer.length > 0) {
                var self = this;
                var form = adoptedActionsContainer.closest('form');
                var actions = adoptedActionsContainer.find('button, input, a');

                if (form.length > 0) {
                    this.form = form;
                    var formAction = this.form.attr('action');
                    if (formAction.length > 0 && formAction[0] !== '#') {
                        this.options.url = formAction;
                    }
                    this.form.submit(function(e) {
                        e.stopImmediatePropagation();
                        self.trigger('adoptedFormSubmit', self.form, self);
                        return false;
                    });
                }

                _.each(actions, function(action, idx) {
                    var $action = $(action);
                    var actionId = $action.data('action-name') || 'adopted_action_' + idx;
                    switch (action.type.toLowerCase()) {
                        case 'submit':
                            actionId = 'form_submit';
                            break;
                        case 'reset':
                            actionId = 'form_reset';
                            break;
                    }
                    self.actions.adopted[actionId] = $action;
                });
                adoptedActionsContainer.remove();
            }
        },

        _getAdoptedActionsContainer: function() {
            if (this.options.actionsEl !== undefined) {
                if (typeof this.options.actionsEl === 'string') {
                    return this.$el.find(this.options.actionsEl);
                } else if (_.isElement(this.options.actionsEl )) {
                    return this.options.actionsEl;
                }
            }
            return false;
        },

        _onAdoptedFormSubmitClick: function(form) {
            form.submit();
        },

        _onAdoptedFormSubmit: function(form) {
            this.loadContent(form.serialize(), form.attr('method'));
        },

        _onAdoptedFormResetClick: function(form) {
            $(form).trigger('reset');
        },

        addAction: function(key, actionElement, section) {
            if (section === undefined) {
                section = 'main';
            }
            if (!this.hasAction(key, section)) {
                this.actions[key] = actionElement;
                this.getActionsElement().append(actionElement);
            }
        },

        getActions: function() {
            return this.actions;
        },

        setUrl: function(url) {
            this.options.url = url;
        },

        removeAction: function(key, section) {
            var self = this,
                remove = function(actions, key) {
                    if (_.isElement(self.actions[key])) {
                        self.actions[key].remove();
                    }
                    delete self.actions[key];
                };
            if (this.hasAction(key, section)) {
                if (section !== undefined) {
                    remove(this.actions[section], key);
                } else {
                    _.each(this.actions, function(actions, section) {
                        if (self.hasAction(key, section)) {
                            remove(actions, key);
                        }
                    });
                }
            }
        },

        hasAction: function(key, section) {
            if (section !== undefined) {
                return this.actions.hasOwnProperty(section) && this.actions[section].hasOwnProperty(key);
            } else {
                var hasAction = false;
                _.each(this.actions, function(actions) {
                    if (actions.hasOwnProperty(key)) {
                        hasAction = true;
                    }
                });
                return hasAction;
            }
        },

        getAction: function(key, section) {
            var action = null;
            if (this.hasAction(key, section)) {
                if (section !== undefined) {
                    action = this.actions[section][key];
                } else {
                    _.each(this.actions, function(actions) {
                        if (actions.hasOwnProperty(key)) {
                            action = actions[key];
                        }
                    });
                }
            }
            return action;
        },

        _renderActions: function() {
            var self = this;
            this._clearActionsContainer();
            var container = this.getActionsElement();

            if (container) {
                _.each(this.actions, function(actions, section) {
                    var sectionContainer = $('
');
                    _.each(actions, function(action) {
                        self._initActionEvents(action);
                        sectionContainer.append(action);
                    });
                    container.append(sectionContainer);
                });
            }
        },

        _initActionEvents: function(action) {
            var self = this;
            switch (action.attr('type').toLowerCase()) {
                case 'submit':
                    action.on('click', function() {
                        self.trigger('adoptedFormSubmitClick', self.form, self);
                        return false;
                    });
                    break;

                case 'reset':
                    action.on('click', function() {
                        self.trigger('adoptedFormResetClick', self.form, self);
                    });
                    break;
            }
        },

        _clearActionsContainer: function() {
            var actionsEl = this.getActionsElement();
            if (actionsEl) {
                actionsEl.empty();
            }
        },

        /**
         * Render widget
         */
        render: function() {
            var loadAllowed = this.$el.html().length == 0 || !this.options.elementFirst ||
                (this.options.elementFirst && !this.firstRun);
            if (loadAllowed && this.options.url !== false) {
                this.loadContent();
            } else {
                this._show();
            }
            this.firstRun = false;
        },

        /**
         * Load content
         *
         * @param {Object|null} data
         * @param {String|null} method
         */
        loadContent: function(data, method) {
            var url = this.options.url;
            if (url === undefined || !url) {
                url = window.location.href;
            }
            if (this.firstRun || method === undefined || !method) {
                method = 'get';
            }
            var options = {
                url: url,
                type: method
            };
            if (data !== undefined) {
                options.data = data;
            }
            options.data = (options.data !== undefined ? options.data + '&' : '') +
                '_widgetContainer=' + this.options.type + '&_wid=' + this.getWid();

            Backbone.$.ajax(options).done(_.bind(function(content) {
                try {
                    this.trigger('contentLoad', content, this);
                    this.actionsEl = null;
                    this.setElement($(content).filter('.widget-content'));
                    this._show();
                    Oro.Events.trigger('hash_navigation_request:complete');
                } catch (error) {
                    // Remove state with unrestorable content
                    this.trigger('contentLoadError', this);
                }
            }, this));
        },

        _show: function() {
            this.trigger('renderStart', this.$el, this);
            this._adoptWidgetActions();
            this.show();
            this.trigger('renderComplete', this.$el, this);
        },

        show: function() {
            this.setWidToElement(this.$el);
            this._renderActions();
            this.trigger('widgetRender', this.$el, this);
        },

        setWidToElement: function(el) {
            el.attr('data-wid', this.getWid());
        }
    });
});
<commit_msg>CRM-485: Buttons for import/export in Contact list - added ability to upload files in widget forms<commit_after>/* jshint devel:true*/
/* global define, require */
define(['underscore', 'backbone', 'oro/mediator', 'jquery.form'],
function(_, Backbone, mediator) {
    'use strict';

    var $ = Backbone.$;

    /**
     * @export  oro/abstract-widget
     * @class   oro.AbstractWidget
     * @extends Backbone.View
     */
    return Backbone.View.extend({
        options: {
            type: 'widget',
            actionsEl: '.widget-actions',
            url: false,
            elementFirst: true,
            title: '',
            alias: null,
            wid: null
        },

        initialize: function(options) {
            options = options || {};
            this.initializeWidget(options);
        },

        setTitle: function(title) {
            console.warn('Implement setTitle');
        },

        getActionsElement: function() {
            console.warn('Implement getActionsElement');
        },

        remove: function() {
            // cause that's circular dependency
            mediator.trigger('widget_remove', this.getWid());
            Backbone.View.prototype.remove.call(this);
        },

        /**
         * Initialize
         */
        initializeWidget: function(options) {
            if (this.options.wid) {
                this._wid = this.options.wid;
            }

            this.on('adoptedFormSubmitClick', _.bind(this._onAdoptedFormSubmitClick, this));
            this.on('adoptedFormResetClick', _.bind(this._onAdoptedFormResetClick, this));
            this.on('adoptedFormSubmit', _.bind(this._onAdoptedFormSubmit, this));

            this.actions = {};
            this.firstRun = true;
        },

        getWid: function() {
            if (!this._wid) {
                this._wid = this._getUniqueIdentifier();
            }
            return this._wid;
        },

        getAlias: function() {
            return this.$el.data('alias') || this.options.alias;
        },

        _getUniqueIdentifier: function() {
            /*jslint bitwise:true */
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0,
                    v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        },

        /**
         * Move form actions to widget actions
         */
        _adoptWidgetActions: function() {
            this.actions['adopted'] = {};
            this.form = null;
            var adoptedActionsContainer = this._getAdoptedActionsContainer();
            if (adoptedActionsContainer.length > 0) {
                var self = this;
                var form = adoptedActionsContainer.closest('form');
                var actions = adoptedActionsContainer.find('button, input, a');

                if (form.length > 0) {
                    this.form = form;
                    var formAction = this.form.attr('action');
                    if (formAction.length > 0 && formAction[0] !== '#') {
                        this.options.url = formAction;
                    }
                    this.form.submit(function(e) {
                        e.stopImmediatePropagation();
                        self.trigger('adoptedFormSubmit', self.form, self);
                        return false;
                    });
                }

                _.each(actions, function(action, idx) {
                    var $action = $(action);
                    var actionId = $action.data('action-name') || 'adopted_action_' + idx;
                    switch (action.type.toLowerCase()) {
                        case 'submit':
                            actionId = 'form_submit';
                            break;
                        case 'reset':
                            actionId = 'form_reset';
                            break;
                    }
                    self.actions.adopted[actionId] = $action;
                });
                adoptedActionsContainer.remove();
            }
        },

        _getAdoptedActionsContainer: function() {
            if (this.options.actionsEl !== undefined) {
                if (typeof this.options.actionsEl === 'string') {
                    return this.$el.find(this.options.actionsEl);
                } else if (_.isElement(this.options.actionsEl )) {
                    return this.options.actionsEl;
                }
            }
            return false;
        },

        _onAdoptedFormSubmitClick: function(form) {
            form.submit();
        },

        _onAdoptedFormSubmit: function(form) {
            if (form.find('[type="file"]').length) {
                form.ajaxSubmit({
                    data: {
                        '_widgetContainer': this.options.type,
                        '_wid': this.getWid()
                    },
                    success: _.bind(this.onContentLoad, this)
                });
            } else {
                this.loadContent(form.serialize(), form.attr('method'));
            }
        },

        _onAdoptedFormResetClick: function(form) {
            $(form).trigger('reset');
        },

        addAction: function(key, actionElement, section) {
            if (section === undefined) {
                section = 'main';
            }
            if (!this.hasAction(key, section)) {
                this.actions[key] = actionElement;
                this.getActionsElement().append(actionElement);
            }
        },

        getActions: function() {
            return this.actions;
        },

        setUrl: function(url) {
            this.options.url = url;
        },

        removeAction: function(key, section) {
            var self = this,
                remove = function(actions, key) {
                    if (_.isElement(self.actions[key])) {
                        self.actions[key].remove();
                    }
                    delete self.actions[key];
                };
            if (this.hasAction(key, section)) {
                if (section !== undefined) {
                    remove(this.actions[section], key);
                } else {
                    _.each(this.actions, function(actions, section) {
                        if (self.hasAction(key, section)) {
                            remove(actions, key);
                        }
                    });
                }
            }
        },

        hasAction: function(key, section) {
            if (section !== undefined) {
                return this.actions.hasOwnProperty(section) && this.actions[section].hasOwnProperty(key);
            } else {
                var hasAction = false;
                _.each(this.actions, function(actions) {
                    if (actions.hasOwnProperty(key)) {
                        hasAction = true;
                    }
                });
                return hasAction;
            }
        },

        getAction: function(key, section) {
            var action = null;
            if (this.hasAction(key, section)) {
                if (section !== undefined) {
                    action = this.actions[section][key];
                } else {
                    _.each(this.actions, function(actions) {
                        if (actions.hasOwnProperty(key)) {
                            action = actions[key];
                        }
                    });
                }
            }
            return action;
        },

        _renderActions: function() {
            var self = this;
            this._clearActionsContainer();
            var container = this.getActionsElement();

            if (container) {
                _.each(this.actions, function(actions, section) {
                    var sectionContainer = $('
');
                    _.each(actions, function(action) {
                        self._initActionEvents(action);
                        sectionContainer.append(action);
                    });
                    container.append(sectionContainer);
                });
            }
        },

        _initActionEvents: function(action) {
            var self = this;
            switch (action.attr('type').toLowerCase()) {
                case 'submit':
                    action.on('click', function() {
                        self.trigger('adoptedFormSubmitClick', self.form, self);
                        return false;
                    });
                    break;

                case 'reset':
                    action.on('click', function() {
                        self.trigger('adoptedFormResetClick', self.form, self);
                    });
                    break;
            }
        },

        _clearActionsContainer: function() {
            var actionsEl = this.getActionsElement();
            if (actionsEl) {
                actionsEl.empty();
            }
        },

        /**
         * Render widget
         */
        render: function() {
            var loadAllowed = this.$el.html().length == 0 || !this.options.elementFirst ||
                (this.options.elementFirst && !this.firstRun);
            if (loadAllowed && this.options.url !== false) {
                this.loadContent();
            } else {
                this._show();
            }
            this.firstRun = false;
        },

        /**
         * Load content
         *
         * @param {Object|null} data
         * @param {String|null} method
         */
        loadContent: function(data, method) {
            var url = this.options.url;
            if (url === undefined || !url) {
                url = window.location.href;
            }
            if (this.firstRun || method === undefined || !method) {
                method = 'get';
            }
            var options = {
                url: url,
                type: method
            };
            if (data !== undefined) {
                options.data = data;
            }
            options.data = (options.data !== undefined ? options.data + '&' : '') +
                '_widgetContainer=' + this.options.type + '&_wid=' + this.getWid();

            Backbone.$.ajax(options).done(_.bind(this.onContentLoad, this));
        },

        /**
         * Handle loaded content.
         *
         * @param {String} content
         */
        onContentLoad: function(content) {
            try {
                this.trigger('contentLoad', content, this);
                this.actionsEl = null;
                this.setElement($(content).filter('.widget-content'));
                this._show();
                Oro.Events.trigger('hash_navigation_request:complete');
            } catch (error) {
                // Remove state with unrestorable content
                this.trigger('contentLoadError', this);
            }
        },

        _show: function() {
            this.trigger('renderStart', this.$el, this);
            this._adoptWidgetActions();
            this.show();
            this.trigger('renderComplete', this.$el, this);
        },

        show: function() {
            this.setWidToElement(this.$el);
            this._renderActions();
            this.trigger('widgetRender', this.$el, this);
        },

        setWidToElement: function(el) {
            el.attr('data-wid', this.getWid());
        }
    });
});
<|endoftext|>


<commit_before>/**
 *
 */

VK.init({
    apiId: 1755175
});

var WRONG_TEXT = 'Something goes wrong. Enter valid post address.';

var grp = '';
var us = '';
var grpAvatar = '';

var obj = [];
var parsed = [];

function getGroupInfo(groupId) {
    var code1 = 'var a=API.likes.getList({"type":"post","owner_id":"-10639516","item_id":"58333241","offset":0,"count":100}); return {"ids":a.items};';

    VK.api('execute', { 'code': code1, }, function (data) {
        console.log(data);
        if (data.response.error) {
            errorMSG('Wrong: Group Member');
            console.log("error");
        } else {
            //group_members_count = data.response.count;
            var bothSex = bothSex.concat(data.response.ids);
            /*for (i = 0; i < bothSex.length; i++) {
                if (bothSex[i] == 1) {
                    manPos.push(i);
                } else {
                    womanPos.push(i);
                }
            }*/

            console.log(data, "bo l: " + bothSex.length, "bo arr: " + bothSex);
            //console.log("m: " + manPos.length);
            //console.log("w: " + womanPos.length);
        }
    });

    /*
    VK.api("groups.getById", { 'group_id': Math.abs(groupId), 'fields': 'members_count,photo_100' }, function (data) {
        if (data.error) {
            errorMSG('Error in data. Wrong group data.')
        }else{
            for (var i = 0; i < data.response.length; i++) {
                grp = data.response[i].name;
                us = data.response[i].members_count;
                grpAvatar = data.response[i].photo_100;

                obj[0] = grp;
                obj[1] = us;
                obj[2] = grpAvatar;

            }
        }
	});*/
    /*
    groupViewChanges(getGroupData());

    setTimeout(function () { waiting(); }, 400);

    getGroupMembers(parsed);*/
}

function getGroupMembers(objTarget) {
    var offset = '0';
    var co = '1000';
    var aboutPost = objTarget; // 0-group, 1-item_post
    var group_members_count = 0;
    var code = '';

    var manPos = []; //man
    var womanPos = []; //woman

    var bothSex = [];

    /*VK.api('likes.getList', {'type':'post','owner_id':info[0],'item_id':info[1],'offset':OFFSET,'count':15}, function (data) {
        if (data.error) { errorMSG('Wrong: Likes fail.'); }

        m_count = data.response.count;
        var t = data.response;
        var members = [];
        members = members.concat(t.users);

        document.getElementById('m_members').innerHTML = 'Count: '+m_count +'
'+ ' users: '+members.length;
        setTimeout(waiting, 333);
    });*/
    console.log("post", aboutPost[0], aboutPost[1]);

    var code1 = 'var a=API.likes.getList({"type":"post","owner_id":"-10639516","item_id":"58333241","offset":0,"count":100}); return {"ids":a.items};';

    code = 'var c=0;var co=100;var p=[];var i=0;var o=0;var u=[];'+
        'while(i!=10) {var li=API.likes.getList({"type":"post","owner_id":'+aboutPost[0]+',"item_id":'+aboutPost[1]+',"filter":"likes","friends_only":0,"offset":o,"count":co});'+
        'c=li.count;u=u+API.users.get({"user_ids":li.items,"fields":"sex"})@.sex;i=i+1;o=o+co;}'+
        'return {"count":c,"user":u,"le":u.length};';

    VK.api('execute', { 'code': code1, }, function (data) {
        console.log(data);
        if (data.response.error) {
            errorMSG('Wrong: Group Member');
            console.log("error");
        } else {
            //group_members_count = data.response.count;
            bothSex = bothSex.concat(data.response.ids);
            /*for (i = 0; i < bothSex.length; i++) {
                if (bothSex[i] == 1) {
                    manPos.push(i);
                } else {
                    womanPos.push(i);
                }
            }*/

            console.log(data,"bo l: "+bothSex.length, "bo arr: "+bothSex);
            console.log("m: " + manPos.length);
            console.log("w: " + womanPos.length);
        }
    });
}

function getGroupData() {
    return obj;
}

function getParsedInfo() {
    return parsed;
}

function waiting() {
    //time
    console.log("waiting");
}

    function groupViewChanges(obj) {
        var a = obj;
        document.getElementById('groupName').innerHTML = 'Name: '+a[0];
        document.getElementById('members_count').innerHTML = 'Count: '+a[1];
        document.getElementById('av').src = a[2];
    }

    function parseLink() {
        var link = String(document.getElementById('post').value);

        if (link != '' && (link.match('vk.com') == 'vk.com') && (link.match('wall-') == 'wall-')) {

            if (link.indexOf('=wall') != -1) {
                var adr = link.substring(link.indexOf('=wall') + 5, link.length);
                var p = adr.split('_');
                if ((p[0] === undefined) && (p[1] === undefined)) {
                    errorMSG('Wrong: group or post or both');
                } else {
                    if ((/\d+/.test(p[0])) && (/\d+/.test(p[1]))) {
                        parsed[0] = p[0]; parsed[1] = p[1];
                        getGroupInfo(parsed[0]);
                    }

                }
            } else {
                errorMSG('Wrong: not wall post');
            }
        }else{
            errorMSG('Wrong: not vk.com');
        }
    }

    function clearadress() {
        document.getElementById('post').value = '';
    }

    function errorMSG(msg) {
        document.getElementById('post').value = msg;
        setTimeout(clearadress, 1000);
    }
<commit_msg>lol changes<commit_after>/**
 *
 */

VK.init({
    apiId: 1755175
});

var WRONG_TEXT = 'Something goes wrong. Enter valid post address.';

var grp = '';
var us = '';
var grpAvatar = '';

var obj = [];
var parsed = [];

function getGroupInfo(groupId) {
    var code1 = 'var a=API.likes.getList({"type":"post","owner_id":"-10639516","item_id":"58333241","offset":0,"count":100}); return {"ids":a.items};';

    VK.Api.call('execute', { 'code': code1, }, function (data) {
        console.log(data);
        if (data.response.error) {
            errorMSG('Wrong: Group Member');
            console.log("error");
        } else {
            //group_members_count = data.response.count;
            var bothSex = bothSex.concat(data.response.ids);
            /*for (i = 0; i < bothSex.length; i++) {
                if (bothSex[i] == 1) {
                    manPos.push(i);
                } else {
                    womanPos.push(i);
                }
            }*/

            console.log(data, "bo l: " + bothSex.length, "bo arr: " + bothSex);
            //console.log("m: " + manPos.length);
            //console.log("w: " + womanPos.length);
        }
    });

    /*
    VK.api("groups.getById", { 'group_id': Math.abs(groupId), 'fields': 'members_count,photo_100' }, function (data) {
        if (data.error) {
            errorMSG('Error in data. Wrong group data.')
        }else{
            for (var i = 0; i < data.response.length; i++) {
                grp = data.response[i].name;
                us = data.response[i].members_count;
                grpAvatar = data.response[i].photo_100;

                obj[0] = grp;
                obj[1] = us;
                obj[2] = grpAvatar;

            }
        }
	});*/
    /*
    groupViewChanges(getGroupData());

    setTimeout(function () { waiting(); }, 400);

    getGroupMembers(parsed);*/
}

function getGroupMembers(objTarget) {
    var offset = '0';
    var co = '1000';
    var aboutPost = objTarget; // 0-group, 1-item_post
    var group_members_count = 0;
    var code = '';

    var manPos = []; //man
    var womanPos = []; //woman

    var bothSex = [];

    /*VK.api('likes.getList', {'type':'post','owner_id':info[0],'item_id':info[1],'offset':OFFSET,'count':15}, function (data) {
        if (data.error) { errorMSG('Wrong: Likes fail.'); }

        m_count = data.response.count;
        var t = data.response;
        var members = [];
        members = members.concat(t.users);

        document.getElementById('m_members').innerHTML = 'Count: '+m_count +'
'+ ' users: '+members.length;
        setTimeout(waiting, 333);
    });*/
    console.log("post", aboutPost[0], aboutPost[1]);

    var code1 = 'var a=API.likes.getList({"type":"post","owner_id":"-10639516","item_id":"58333241","offset":0,"count":100}); return {"ids":a.items};';

    code = 'var c=0;var co=100;var p=[];var i=0;var o=0;var u=[];'+
        'while(i!=10) {var li=API.likes.getList({"type":"post","owner_id":'+aboutPost[0]+',"item_id":'+aboutPost[1]+',"filter":"likes","friends_only":0,"offset":o,"count":co});'+
        'c=li.count;u=u+API.users.get({"user_ids":li.items,"fields":"sex"})@.sex;i=i+1;o=o+co;}'+
        'return {"count":c,"user":u,"le":u.length};';

    VK.api('execute', { 'code': code1, }, function (data) {
        console.log(data);
        if (data.response.error) {
            errorMSG('Wrong: Group Member');
            console.log("error");
        } else {
            //group_members_count = data.response.count;
            bothSex = bothSex.concat(data.response.ids);
            /*for (i = 0; i < bothSex.length; i++) {
                if (bothSex[i] == 1) {
                    manPos.push(i);
                } else {
                    womanPos.push(i);
                }
            }*/

            console.log(data,"bo l: "+bothSex.length, "bo arr: "+bothSex);
            console.log("m: " + manPos.length);
            console.log("w: " + womanPos.length);
        }
    });
}

function getGroupData() {
    return obj;
}

function getParsedInfo() {
    return parsed;
}

function waiting() {
    //time
    console.log("waiting");
}

    function groupViewChanges(obj) {
        var a = obj;
        document.getElementById('groupName').innerHTML = 'Name: '+a[0];
        document.getElementById('members_count').innerHTML = 'Count: '+a[1];
        document.getElementById('av').src = a[2];
    }

    function parseLink() {
        var link = String(document.getElementById('post').value);

        if (link != '' && (link.match('vk.com') == 'vk.com') && (link.match('wall-') == 'wall-')) {

            if (link.indexOf('=wall') != -1) {
                var adr = link.substring(link.indexOf('=wall') + 5, link.length);
                var p = adr.split('_');
                if ((p[0] === undefined) && (p[1] === undefined)) {
                    errorMSG('Wrong: group or post or both');
                } else {
                    if ((/\d+/.test(p[0])) && (/\d+/.test(p[1]))) {
                        parsed[0] = p[0]; parsed[1] = p[1];
                        getGroupInfo(parsed[0]);
                    }

                }
            } else {
                errorMSG('Wrong: not wall post');
            }
        }else{
            errorMSG('Wrong: not vk.com');
        }
    }

    function clearadress() {
        document.getElementById('post').value = '';
    }

    function errorMSG(msg) {
        document.getElementById('post').value = msg;
        setTimeout(clearadress, 1000);
    }
<|endoftext|>


<commit_before>/* light-i18n main */
/* jshint browser: true */
// Base function.
(function (global) {
  "use strict";

  var languageDialect = (function(lang) {
  		window.location.search.slice(1).split("&").some(function (searchTerm) {
  			if (searchTerm.indexOf("lang=") === 0) {
  				lang = searchTerm.split("=").slice(1).join("=");
  				return true;
  			}
  		});

  		return lang.toLowerCase();
  	})(window.navigator.userLanguage || window.navigator.language),
    language = languageDialect.split("-")[0],
    translations;

  function loadTranslations(language, set, base) {
    var url = (base || "locales/") + language + "/" + (set || "translation") + ".json";
    console.info("loading translations from: " + url);

    return fetch(url, {
      headers: {
        "Accept": "application/json"
      }
    }).then(function (res) {
      if (res.ok) {
        console.log("successfully loaded translations");
        return res.json();
      }

      // TODO add error object instead of false
      return Promise.reject(false);
    }).catch(function (err) {
      console.error("Error loading translations: %o", err);
    });
  }

  function loadAndApplyTranslations(language, ancestor, set, base) {
    return loadTranslations(language, set, base).then(function (obj) {
      translate(obj, ancestor);
      return obj;
    });
  }

  function applyTranslationToElement(ele, obj) {
    if(ele.hasAttribute("i18n")) {
      applyTranslation(ele, ele.getAttribute("i18n"), obj);
    }
  }

  function applyTranslation(ele, path, obj) {
		var translated = getByPath(obj, path);

    if (typeof(translated) !== "undefined") {
			ele.textContent = translated;
    } else {
      console.log("Could not translate website: path '%s' not found", path);
    }
  }

  function getLang(ele, threshold) {
    do {
      if(ele.lang) {
        return ele.lang;
      }
    } while((ele = ele.parentElement) && ele !== threshold);
  }

  function getByPath(obj, path) {
    return path.split(".").reduce(function (obj, key) {
      return obj ? obj[key] : undefined;
    }, obj);
  }

  function translate(obj, ancestor) {
    ancestor = ancestor || document;

    if(getLang(ancestor) === languageDialect) {
      if(!ancestor.hasAttribute("i18n")) {
        [].slice.call(ancestor.querySelectorAll("[lang]:not([lang='" + language + "'])")).forEach(translate.bind(null, obj));
      }
      return;
    }

    if(ancestor.hasAttribute("i18n")) {
      applyTranslationToElement(ancestor, obj);
    } else {
      [].slice.call(ancestor.querySelectorAll("[i18n]")).forEach(function(ele) {
        if(getLang(ele, ancestor) !== languageDialect) {
          applyTranslationToElement(ele, obj);
        }
      });
    }
  }

  translations = loadAndApplyTranslations(language);

  global.i18n = {
    translations: translations,
    translate: function(ele) {
      return this.translations.then(function(obj) {
        translate(obj, ele);
        return obj;
      });
    },
    set language(lang) {
			lang = String(lang);

			if(lang !== language) {
      	language = lang;
        translations = loadAndApplyTranslations(lang);
			}
    },
    get language() {
      return language;
    }
  };
}(this));
<commit_msg>Fix error when translating entire document<commit_after>(function (global) {
  "use strict";

  var languageDialect = (function(lang) {
  		window.location.search.slice(1).split("&").some(function (searchTerm) {
  			if (searchTerm.indexOf("lang=") === 0) {
  				lang = searchTerm.split("=").slice(1).join("=");
  				return true;
  			}
  		});

  		return lang.toLowerCase();
  	})(window.navigator.userLanguage || window.navigator.language),
    language = languageDialect.split("-")[0],
    translations;

  function loadTranslations(language, set, base) {
    var url = (base || "locales/") + language + "/" + (set || "translation") + ".json";
    console.info("loading translations from: " + url);

    return fetch(url, {
      headers: {
        "Accept": "application/json"
      }
    }).then(function (res) {
      if (res.status >= 200 && res.status < 300) {
        console.info("successfully loaded translations");
        return res.json();
      }

      // TODO add error object instead of false
      return Promise.reject(false);
    }).catch(function (err) {
      console.error("Error loading translations: %o", err);
    });
  }

  function loadAndApplyTranslations(language, ancestor, set, base) {
    return loadTranslations(language, set, base).then(function (obj) {
      translate(obj, ancestor);
      return obj;
    });
  }

  function applyTranslationToElement(ele, obj) {
    if(ele.hasAttribute("i18n")) {
      applyTranslation(ele, ele.getAttribute("i18n"), obj);
    }
  }

  function applyTranslation(ele, path, obj) {
		var translated = getByPath(obj, path);

    if (typeof(translated) !== "undefined") {
			ele.textContent = translated;
    } else {
      console.warn("Could not translate website: path '%s' not found", path);
    }
  }

  function getLang(ele, threshold) {
    do {
      if(ele.lang) {
        return ele.lang;
      }
    } while((ele = ele.parentElement) && ele !== threshold);
  }

  function getByPath(obj, path) {
    return path.split(".").reduce(function (obj, key) {
      return obj ? obj[key] : undefined;
    }, obj);
  }

  function translate(obj, ancestor) {
    ancestor = ancestor || document.documentElement;

    if(getLang(ancestor) === languageDialect) {
      if(!ancestor.hasAttribute("i18n")) {
        [].slice.call(ancestor.querySelectorAll("[lang]:not([lang='" + language + "'])")).forEach(translate.bind(null, obj));
      }
      return;
    }

    if(ancestor.hasAttribute("i18n")) {
      applyTranslationToElement(ancestor, obj);
    } else {
      [].slice.call(ancestor.querySelectorAll("[i18n]")).forEach(function(ele) {
        if(getLang(ele, ancestor) !== languageDialect) {
          applyTranslationToElement(ele, obj);
        }
      });
    }
  }

  translations = loadAndApplyTranslations(language);

  global.i18n = {
    translations: translations,
    translate: function(ele) {
      return this.translations.then(function(obj) {
        translate(obj, ele);
        return obj;
      });
    },
    set language(lang) {
			lang = String(lang);

			if(lang !== language) {
      	language = lang;
        translations = loadAndApplyTranslations(lang);
			}
    },
    get language() {
      return language;
    }
  };
}(this));
<|endoftext|>