<commit_before>//v0.4e test
startSpecialEffectSkin();
$(document).ready(function() {
	setTimeout(function() {
    $("#imgur img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });

    $("#hizliresim img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });
     $("#lowres img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });
     $("#vanillaskins img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });

    $("img.lazy").on("click", function(e) {
        e.preventDefault();
		var url = $(this).attr("name");
        $("#skin-url").val(url).select();
        try {
        } catch (e) {}
    });

    $("#skin-url").on("click", function(e) {
        $(this).select();
        try {
        } catch (e) {}
    });

	$("#nav a").on("click", function() {
        var _this = $(this);
        if (_this.hasClass("link")) {
            return;
        }
        $("#nav a").removeClass("active");
        _this.addClass("active");
        var id = _this.attr("href");
        $(".skins-wrapper").hide();
        $(id).show();
        $(window).scrollTop(0).trigger("scroll");
        return false;
    });
    $("#UseEffect").on("click", function(e) {
		loadSpecialEffectSkin(e)
    });
    $("#UseEffect2").on("click", function(e) {
		toastr.info("[SERVER]: Special Effect erased");
		if (document.getElementsByClassName("skins-wrapper")[2].children[0]) document.getElementsByClassName("skins-wrapper")[2].children[0].remove()
			SpecialEffectPlayers[application.lastSentNick]=null
			localStorage.setItem("isActualUsingSpecialEffectsSkin", null);
			window.application.sendSocket3Info("spfc", null)
    });
	setTimeout(function() {
		$("#nav a")[0].click();
	}, 950);
	}, 900);
});

function startSpecialEffectSkin(){
	if (localStorage.getItem("isActualUsingSpecialEffectsSkin")){
			try {
			for (var i = 0; i < $(".skins-wrapper").length; i++){
				if ($(".skins-wrapper")[i].style.display != "none"){
					for (var j = 0; j < document.getElementsByClassName("skins-wrapper")[i].children.length; j++)
						if (document.getElementsByClassName("skins-wrapper")[i].children[j].children[0].name ==localStorage.getItem("isActualUsingSpecialEffectsSkin")){
							if (document.getElementsByClassName("skins-wrapper")[2].children[0]) document.getElementsByClassName("skins-wrapper")[2].children[0].remove()
							document.getElementsByClassName("skins-wrapper")[2].append(document.getElementsByClassName("skins-wrapper")[i].children[j])
						}
				}
			}
			application.lastSentNick = $("#nick").val()
			SpecialEffectPlayers[application.lastSentNick]=localStorage.getItem("isActualUsingSpecialEffectsSkin")
			//localStorage.setItem("isActualUsingSpecialEffectsSkin", $("#skin-url").val());
			window.application.sendSocket3Info("spfc", localStorage.getItem("isActualUsingSpecialEffectsSkin"))

			} catch (e) {}
	}
}

function loadSpecialEffectSkin(e){
		if ($("#nick").val().includes('℄') || window.proLicenceUID){
			try {
			toastr.info("[SERVER]: Special Effect " + $("#skin-url").val() + " activated");
			for (var i = 0; i < $(".skins-wrapper").length; i++){
				if ($(".skins-wrapper")[i].style.display != "none"){
					for (var j = 0; j < document.getElementsByClassName("skins-wrapper")[i].children.length; j++)
						if (document.getElementsByClassName("skins-wrapper")[i].children[j].children[0].name ==$("#skin-url").val()){
							if (document.getElementsByClassName("skins-wrapper")[2].children[0]) document.getElementsByClassName("skins-wrapper")[2].children[0].remove()
							document.getElementsByClassName("skins-wrapper")[2].append(document.getElementsByClassName("skins-wrapper")[i].children[j])
						}
				}
			}
			application.lastSentNick = $("#nick").val()
			SpecialEffectPlayers[application.lastSentNick]=$("#skin-url").val()
			localStorage.setItem("isActualUsingSpecialEffectsSkin", $("#skin-url").val());
			window.application.sendSocket3Info("spfc", $("#skin-url").val())

			} catch (e) {}
		}
		else{
			toastr.warning("[SERVER]: Not Premium account found. If you donated in the past, please refer it to Legend mod discord.
Thank you for using Legend mod!").css("width", "350px");
		}
}
<commit_msg>Update shopscript.js<commit_after>//v0.4d test
$(document).ready(function() {
	setTimeout(function() {
    $("#imgur img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });

    $("#hizliresim img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });
     $("#lowres img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });
     $("#vanillaskins img.lazy").lazyload({
        effect : "fadeIn",
        skip_invisible : true
    });

    $("img.lazy").on("click", function(e) {
        e.preventDefault();
		var url = $(this).attr("name");
        $("#skin-url").val(url).select();
        try {
        } catch (e) {}
    });

    $("#skin-url").on("click", function(e) {
        $(this).select();
        try {
        } catch (e) {}
    });

	$("#nav a").on("click", function() {
        var _this = $(this);
        if (_this.hasClass("link")) {
            return;
        }
        $("#nav a").removeClass("active");
        _this.addClass("active");
        var id = _this.attr("href");
        $(".skins-wrapper").hide();
        $(id).show();
        $(window).scrollTop(0).trigger("scroll");
        return false;
    });
    $("#UseEffect").on("click", function(e) {
		loadSpecialEffectSkin(e)
    });
    $("#UseEffect2").on("click", function(e) {
		toastr.info("[SERVER]: Special Effect erased");
		if (document.getElementsByClassName("skins-wrapper")[2].children[0]) document.getElementsByClassName("skins-wrapper")[2].children[0].remove()
			SpecialEffectPlayers[application.lastSentNick]=null
			localStorage.setItem("isActualUsingSpecialEffectsSkin", null);
			window.application.sendSocket3Info("spfc", null)
    });
	setTimeout(function() {
		$("#nav a")[0].click();
		startSpecialEffectSkin();
	}, 950);
	}, 900);
});

function startSpecialEffectSkin(){
	if (localStorage.getItem("isActualUsingSpecialEffectsSkin")){
			try {
			for (var i = 0; i < $(".skins-wrapper").length; i++){
				if ($(".skins-wrapper")[i].style.display != "none"){
					for (var j = 0; j < document.getElementsByClassName("skins-wrapper")[i].children.length; j++)
						if (document.getElementsByClassName("skins-wrapper")[i].children[j].children[0].name ==localStorage.getItem("isActualUsingSpecialEffectsSkin")){
							if (document.getElementsByClassName("skins-wrapper")[2].children[0]) document.getElementsByClassName("skins-wrapper")[2].children[0].remove()
							document.getElementsByClassName("skins-wrapper")[2].append(document.getElementsByClassName("skins-wrapper")[i].children[j])
						}
				}
			}
			application.lastSentNick = $("#nick").val()
			SpecialEffectPlayers[application.lastSentNick]=localStorage.getItem("isActualUsingSpecialEffectsSkin")
			//localStorage.setItem("isActualUsingSpecialEffectsSkin", $("#skin-url").val());
			window.application.sendSocket3Info("spfc", localStorage.getItem("isActualUsingSpecialEffectsSkin"))

			} catch (e) {}
	}
}

function loadSpecialEffectSkin(e){
		if ($("#nick").val().includes('℄') || window.proLicenceUID){
			try {
			toastr.info("[SERVER]: Special Effect " + $("#skin-url").val() + " activated");
			for (var i = 0; i < $(".skins-wrapper").length; i++){
				if ($(".skins-wrapper")[i].style.display != "none"){
					for (var j = 0; j < document.getElementsByClassName("skins-wrapper")[i].children.length; j++)
						if (document.getElementsByClassName("skins-wrapper")[i].children[j].children[0].name ==$("#skin-url").val()){
							if (document.getElementsByClassName("skins-wrapper")[2].children[0]) document.getElementsByClassName("skins-wrapper")[2].children[0].remove()
							document.getElementsByClassName("skins-wrapper")[2].append(document.getElementsByClassName("skins-wrapper")[i].children[j])
						}
				}
			}
			application.lastSentNick = $("#nick").val()
			SpecialEffectPlayers[application.lastSentNick]=$("#skin-url").val()
			localStorage.setItem("isActualUsingSpecialEffectsSkin", $("#skin-url").val());
			window.application.sendSocket3Info("spfc", $("#skin-url").val())

			} catch (e) {}
		}
		else{
			toastr.warning("[SERVER]: Not Premium account found. If you donated in the past, please refer it to Legend mod discord.
Thank you for using Legend mod!").css("width", "350px");
		}
}
<|endoftext|>


<commit_before>'use strict';

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

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

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

module.exports = class bitz extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bitz',
            'name': 'Bit-Z',
            'countries': 'HK',
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'fetchTickers': true,
                'fetchOHLCV': true,
                'fetchOpenOrders': true,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '1d': '1d',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/35862606-4f554f14-0b5d-11e8-957d-35058c504b6f.jpg',
                'api': 'https://www.bit-z.com/api_v1',
                'www': 'https://www.bit-z.com',
                'doc': 'https://www.bit-z.com/api.html',
                'fees': 'https://www.bit-z.com/about/fee',
            },
            'api': {
                'public': {
                    'get': [
                        'ticker',
                        'tickerall',
                        'depth',
                        'orders',
                        'kline',
                    ],
                },
                'private': {
                    'post': [
                        'balances',
                        'tradeAdd',
                        'tradeCancel',
                        'openOrders',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.001,
                    'taker': 0.001,
                },
                'funding': {
                    'withdraw': {
                        'BTC': '0.5%',
                        'DKKT': '0.5%',
                        'ETH': 0.01,
                        'USDT': '0.5%',
                        'LTC': '0.5%',
                        'FCT': '0.5%',
                        'LSK': '0.5%',
                        'HXI': '0.8%',
                        'ZEC': '0.5%',
                        'DOGE': '0.5%',
                        'MZC': '0.5%',
                        'ETC': '0.5%',
                        'GXS': '0.5%',
                        'XPM': '0.5%',
                        'PPC': '0.5%',
                        'BLK': '0.5%',
                        'XAS': '0.5%',
                        'HSR': '0.5%',
                        'NULS': 5.0,
                        'VOISE': 350.0,
                        'PAY': 1.5,
                        'EOS': 0.6,
                        'YBCT': 35.0,
                        'OMG': 0.3,
                        'OTN': 0.4,
                        'BTX': '0.5%',
                        'QTUM': '0.5%',
                        'DASH': '0.5%',
                        'GAME': '0.5%',
                        'BCH': '0.5%',
                        'GNT': 9.0,
                        'SSS': 1500.0,
                        'ARK': '0.5%',
                        'PART': '0.5%',
                        'LEO': '0.5%',
                        'DGB': '0.5%',
                        'ZSC': 130.0,
                        'VIU': 350.0,
                        'BTG': '0.5%',
                        'ARN': 10.0,
                        'VTC': '0.5%',
                        'BCD': '0.5%',
                        'TRX': 200.0,
                        'HWC': '0.5%',
                        'UNIT': '0.5%',
                        'OXY': '0.5%',
                        'MCO': 0.3500,
                        'SBTC': '0.5%',
                        'BCX': '0.5%',
                        'ETF': '0.5%',
                        'PYLNT': 0.4000,
                        'XRB': '0.5%',
                        'ETP': '0.5%',
                    },
                },
            },
            'precision': {
                'amount': 8,
                'price': 8,
            },
            'options': {
                'lastNonceTimestamp': 0,
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetTickerall ();
        let markets = response['data'];
        let ids = Object.keys (markets);
        let result = [];
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = markets[id];
            let [ baseId, quoteId ] = id.split ('_');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': true,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostBalances (params);
        let data = response['data'];
        let balances = this.omit (data, 'uid');
        let result = { 'info': response };
        let keys = Object.keys (balances);
        for (let i = 0; i < keys.length; i++) {
            let currency = keys[i];
            let balance = parseFloat (balances[currency]);
            if (currency in this.currencies_by_id)
                currency = this.currencies_by_id[currency]['code'];
            else
                currency = currency.toUpperCase ();
            let account = this.account ();
            account['free'] = balance;
            account['used'] = undefined;
            account['total'] = balance;
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['date'] * 1000;
        let symbol = market['symbol'];
        let last = parseFloat (ticker['last']);
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['high']),
            'low': parseFloat (ticker['low']),
            'bid': parseFloat (ticker['buy']),
            'bidVolume': undefined,
            'ask': parseFloat (ticker['sell']),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['vol']),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

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

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

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetDepth (this.extend ({
            'coin': this.marketId (symbol),
        }, params));
        let orderbook = response['data'];
        let timestamp = orderbook['date'] * 1000;
        return this.parseOrderBook (orderbook, timestamp);
    }

    parseTrade (trade, market = undefined) {
        let hkt = this.sum (this.milliseconds (), 28800000);
        let utcDate = this.iso8601 (hkt);
        utcDate = utcDate.split ('T');
        utcDate = utcDate[0] + ' ' + trade['t'] + '+08';
        let timestamp = this.parse8601 (utcDate);
        let price = parseFloat (trade['p']);
        let amount = parseFloat (trade['n']);
        let symbol = market['symbol'];
        let cost = this.priceToPrecision (symbol, amount * price);
        return {
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'id': undefined,
            'order': undefined,
            'type': 'limit',
            'side': trade['s'],
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
            'info': trade,
        };
    }

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

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetKline (this.extend ({
            'coin': market['id'],
            'type': this.timeframes[timeframe],
        }, params));
        let ohlcv = JSON.parse (response['data']['datas']['data']);
        return this.parseOHLCVs (ohlcv, market, timeframe, since, limit);
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (typeof market !== 'undefined')
            symbol = market['symbol'];
        let side = this.safeString (order, 'side');
        if (typeof side === 'undefined') {
            side = this.safeString (order, 'type');
            if (typeof side !== 'undefined')
                side = (side === 'in') ? 'buy' : 'sell';
        }
        let timestamp = undefined;
        let iso8601 = undefined;
        if ('datetime' in order) {
            timestamp = this.parse8601 (order['datetime']);
            iso8601 = this.iso8601 (timestamp);
        }
        return {
            'id': order['id'],
            'datetime': iso8601,
            'timestamp': timestamp,
            'status': 'open',
            'symbol': symbol,
            'type': 'limit',
            'side': side,
            'price': order['price'],
            'cost': undefined,
            'amount': order['number'],
            'filled': undefined,
            'remaining': undefined,
            'trades': undefined,
            'fee': undefined,
            'info': order,
        };
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let orderType = (side === 'buy') ? 'in' : 'out';
        let request = {
            'coin': market['id'],
            'type': orderType,
            'price': this.priceToPrecision (symbol, price),
            'number': this.amountToString (symbol, amount),
            'tradepwd': this.password,
        };
        let response = await this.privatePostTradeAdd (this.extend (request, params));
        let id = response['data']['id'];
        let order = this.parseOrder ({
            'id': id,
            'price': price,
            'number': amount,
            'side': side,
        }, market);
        this.orders[id] = order;
        return order;
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostTradeCancel (this.extend ({
            'id': id,
        }, params));
        return response;
    }

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

    nonce () {
        let currentTimestamp = this.seconds ();
        if (currentTimestamp > this.options['lastNonceTimestamp']) {
            this.options['lastNonceTimestamp'] = currentTimestamp;
            this.options['lastNonce'] = 100000;
        }
        this.options['lastNonce'] += 1;
        return this.options['lastNonce'];
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + path;
        let query = undefined;
        if (api === 'public') {
            query = this.urlencode (params);
            if (query.length)
                url += '?' + query;
        } else {
            this.checkRequiredCredentials ();
            body = this.urlencode (this.keysort (this.extend ({
                'api_key': this.apiKey,
                'timestamp': this.seconds (),
                'nonce': this.nonce (),
            }, params)));
            body += '&sign=' + this.hash (this.encode (body + this.secret));
            headers = { 'Content-type': 'application/x-www-form-urlencoded' };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        let code = this.safeString (response, 'code');
        if (code !== '0') {
            let ErrorClass = this.safeValue ({
                '103': AuthenticationError,
                '104': AuthenticationError,
                '200': AuthenticationError,
                '202': AuthenticationError,
                '401': AuthenticationError,
                '406': AuthenticationError,
                '203': InvalidNonce,
                '201': OrderNotFound,
                '408': InsufficientFunds,
                '106': DDoSProtection,
            }, code, ExchangeError);
            let message = this.safeString (response, 'msg', 'Error');
            throw new ErrorClass (message);
        }
        return response;
    }
};
<commit_msg>bitz fetchBalance update & fix #2537<commit_after>'use strict';

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

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

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

module.exports = class bitz extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bitz',
            'name': 'Bit-Z',
            'countries': 'HK',
            'rateLimit': 1000,
            'version': 'v1',
            'userAgent': this.userAgents['chrome'],
            'has': {
                'fetchTickers': true,
                'fetchOHLCV': true,
                'fetchOpenOrders': true,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '1d': '1d',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/35862606-4f554f14-0b5d-11e8-957d-35058c504b6f.jpg',
                'api': 'https://www.bit-z.com/api_v1',
                'www': 'https://www.bit-z.com',
                'doc': 'https://www.bit-z.com/api.html',
                'fees': 'https://www.bit-z.com/about/fee',
            },
            'api': {
                'public': {
                    'get': [
                        'ticker',
                        'tickerall',
                        'depth',
                        'orders',
                        'kline',
                    ],
                },
                'private': {
                    'post': [
                        'balances',
                        'tradeAdd',
                        'tradeCancel',
                        'openOrders',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.001,
                    'taker': 0.001,
                },
                'funding': {
                    'withdraw': {
                        'BTC': '0.5%',
                        'DKKT': '0.5%',
                        'ETH': 0.01,
                        'USDT': '0.5%',
                        'LTC': '0.5%',
                        'FCT': '0.5%',
                        'LSK': '0.5%',
                        'HXI': '0.8%',
                        'ZEC': '0.5%',
                        'DOGE': '0.5%',
                        'MZC': '0.5%',
                        'ETC': '0.5%',
                        'GXS': '0.5%',
                        'XPM': '0.5%',
                        'PPC': '0.5%',
                        'BLK': '0.5%',
                        'XAS': '0.5%',
                        'HSR': '0.5%',
                        'NULS': 5.0,
                        'VOISE': 350.0,
                        'PAY': 1.5,
                        'EOS': 0.6,
                        'YBCT': 35.0,
                        'OMG': 0.3,
                        'OTN': 0.4,
                        'BTX': '0.5%',
                        'QTUM': '0.5%',
                        'DASH': '0.5%',
                        'GAME': '0.5%',
                        'BCH': '0.5%',
                        'GNT': 9.0,
                        'SSS': 1500.0,
                        'ARK': '0.5%',
                        'PART': '0.5%',
                        'LEO': '0.5%',
                        'DGB': '0.5%',
                        'ZSC': 130.0,
                        'VIU': 350.0,
                        'BTG': '0.5%',
                        'ARN': 10.0,
                        'VTC': '0.5%',
                        'BCD': '0.5%',
                        'TRX': 200.0,
                        'HWC': '0.5%',
                        'UNIT': '0.5%',
                        'OXY': '0.5%',
                        'MCO': 0.3500,
                        'SBTC': '0.5%',
                        'BCX': '0.5%',
                        'ETF': '0.5%',
                        'PYLNT': 0.4000,
                        'XRB': '0.5%',
                        'ETP': '0.5%',
                    },
                },
            },
            'precision': {
                'amount': 8,
                'price': 8,
            },
            'options': {
                'lastNonceTimestamp': 0,
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetTickerall ();
        let markets = response['data'];
        let ids = Object.keys (markets);
        let result = [];
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = markets[id];
            let [ baseId, quoteId ] = id.split ('_');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': true,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostBalances (params);
        let data = response['data'];
        let balances = this.omit (data, 'uid');
        let result = { 'info': response };
        let keys = Object.keys (balances);
        for (let i = 0; i < keys.length; i++) {
            let id = keys[i];
            if (!(id.indexOf ('_') >= 0)) {
                let code = id.toUpperCase ();
                if (id in this.currencies_by_id) {
                    code = this.currencies_by_id[id]['code'];
                }
                let account = this.account ();
                account['used'] = parseFloat (balances[id + '_lock']);
                account['total'] = parseFloat (balances[id]);
                account['free'] = account['total'] - account['used'];
                result[code] = account;
            }
        }
        return this.parseBalance (result);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['date'] * 1000;
        let symbol = market['symbol'];
        let last = parseFloat (ticker['last']);
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['high']),
            'low': parseFloat (ticker['low']),
            'bid': parseFloat (ticker['buy']),
            'bidVolume': undefined,
            'ask': parseFloat (ticker['sell']),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['vol']),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

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

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

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetDepth (this.extend ({
            'coin': this.marketId (symbol),
        }, params));
        let orderbook = response['data'];
        let timestamp = orderbook['date'] * 1000;
        return this.parseOrderBook (orderbook, timestamp);
    }

    parseTrade (trade, market = undefined) {
        let hkt = this.sum (this.milliseconds (), 28800000);
        let utcDate = this.iso8601 (hkt);
        utcDate = utcDate.split ('T');
        utcDate = utcDate[0] + ' ' + trade['t'] + '+08';
        let timestamp = this.parse8601 (utcDate);
        let price = parseFloat (trade['p']);
        let amount = parseFloat (trade['n']);
        let symbol = market['symbol'];
        let cost = this.priceToPrecision (symbol, amount * price);
        return {
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'id': undefined,
            'order': undefined,
            'type': 'limit',
            'side': trade['s'],
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
            'info': trade,
        };
    }

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

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetKline (this.extend ({
            'coin': market['id'],
            'type': this.timeframes[timeframe],
        }, params));
        let ohlcv = JSON.parse (response['data']['datas']['data']);
        return this.parseOHLCVs (ohlcv, market, timeframe, since, limit);
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (typeof market !== 'undefined')
            symbol = market['symbol'];
        let side = this.safeString (order, 'side');
        if (typeof side === 'undefined') {
            side = this.safeString (order, 'type');
            if (typeof side !== 'undefined')
                side = (side === 'in') ? 'buy' : 'sell';
        }
        let timestamp = undefined;
        let iso8601 = undefined;
        if ('datetime' in order) {
            timestamp = this.parse8601 (order['datetime']);
            iso8601 = this.iso8601 (timestamp);
        }
        return {
            'id': order['id'],
            'datetime': iso8601,
            'timestamp': timestamp,
            'status': 'open',
            'symbol': symbol,
            'type': 'limit',
            'side': side,
            'price': order['price'],
            'cost': undefined,
            'amount': order['number'],
            'filled': undefined,
            'remaining': undefined,
            'trades': undefined,
            'fee': undefined,
            'info': order,
        };
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let orderType = (side === 'buy') ? 'in' : 'out';
        let request = {
            'coin': market['id'],
            'type': orderType,
            'price': this.priceToPrecision (symbol, price),
            'number': this.amountToString (symbol, amount),
            'tradepwd': this.password,
        };
        let response = await this.privatePostTradeAdd (this.extend (request, params));
        let id = response['data']['id'];
        let order = this.parseOrder ({
            'id': id,
            'price': price,
            'number': amount,
            'side': side,
        }, market);
        this.orders[id] = order;
        return order;
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostTradeCancel (this.extend ({
            'id': id,
        }, params));
        return response;
    }

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

    nonce () {
        let currentTimestamp = this.seconds ();
        if (currentTimestamp > this.options['lastNonceTimestamp']) {
            this.options['lastNonceTimestamp'] = currentTimestamp;
            this.options['lastNonce'] = 100000;
        }
        this.options['lastNonce'] += 1;
        return this.options['lastNonce'];
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + path;
        let query = undefined;
        if (api === 'public') {
            query = this.urlencode (params);
            if (query.length)
                url += '?' + query;
        } else {
            this.checkRequiredCredentials ();
            body = this.urlencode (this.keysort (this.extend ({
                'api_key': this.apiKey,
                'timestamp': this.seconds (),
                'nonce': this.nonce (),
            }, params)));
            body += '&sign=' + this.hash (this.encode (body + this.secret));
            headers = { 'Content-type': 'application/x-www-form-urlencoded' };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        let code = this.safeString (response, 'code');
        if (code !== '0') {
            let ErrorClass = this.safeValue ({
                '103': AuthenticationError,
                '104': AuthenticationError,
                '200': AuthenticationError,
                '202': AuthenticationError,
                '401': AuthenticationError,
                '406': AuthenticationError,
                '203': InvalidNonce,
                '201': OrderNotFound,
                '408': InsufficientFunds,
                '106': DDoSProtection,
            }, code, ExchangeError);
            let message = this.safeString (response, 'msg', 'Error');
            throw new ErrorClass (message);
        }
        return response;
    }
};
<|endoftext|>


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

  angular.module('dimApp')
    .factory('dimSettingsService', SettingsService);

  SettingsService.$inject = ['$q', '$rootScope', 'uuid2'];

  function SettingsService($q, $rootScope, uuid2) {
    var settingState;

    return {
      'getSetting': getSetting,
      'saveSetting': saveSetting
    };

    function loadSettings() {
      settingState = {
        itemDetails: false,
        condensed: false,
        characterOrder: 'mostRecent',
        itemDetails: false
      };

      return $q(function(resolve, reject) {

        function processStorageSettings(data) {
          if (_.has(data, "settings-v1.0")) {
            resolve(data["settings-v1.0"]);
          } else {
            resolve({
              hideFilteredItems: false,
              condensed: false,
              characterOrder: 'mostRecent',
              itemDetails: false
            });
          }
        }

        chrome.storage.sync.get(null, processStorageSettings);
      });
    }

    function getSetting(key) {
      return loadSettings()
        .then(function(settings) {
          if (_.isUndefined(key)) {
            return settings;
          } else if (_.has(settings, key)) {
            return _.propertyOf(settings)(key);
          } else {
            return $q.reject("The key is not defined in the settings.");
          }
        });
    }

    function saveSetting(key, value) {
      return getSetting()
        .then(function(settings) {
          settings[key] = value;
          var data = {};
          var kvp = {};

          kvp[key] = value;

          data["settings-v1.0"] = settings;

          chrome.storage.sync.set(data, function(e) {
            if (chrome.runtime.lastError) {
              $q.reject(chrome.runtime.lastError);
            } else {
              $rootScope.$broadcast('dim-settings-updated', kvp);
              return true;
            }
          });
        });
    }
  }
})();
<commit_msg>Fixed strict bug w/ duplicate property.<commit_after>(function() {
  'use strict';

  angular.module('dimApp')
    .factory('dimSettingsService', SettingsService);

  SettingsService.$inject = ['$q', '$rootScope', 'uuid2'];

  function SettingsService($q, $rootScope, uuid2) {
    var settingState;

    return {
      'getSetting': getSetting,
      'saveSetting': saveSetting
    };

    function loadSettings() {
      settingState = {
        hideFilteredItems: false,
        itemDetails: false,
        condensed: false,
        characterOrder: 'mostRecent'
      };

      return $q(function(resolve, reject) {

        function processStorageSettings(data) {
          if (_.has(data, "settings-v1.0")) {
            resolve(data["settings-v1.0"]);
          } else {
            resolve({
              hideFilteredItems: false,
              condensed: false,
              characterOrder: 'mostRecent',
              itemDetails: false
            });
          }
        }

        chrome.storage.sync.get(null, processStorageSettings);
      });
    }

    function getSetting(key) {
      return loadSettings()
        .then(function(settings) {
          if (_.isUndefined(key)) {
            return settings;
          } else if (_.has(settings, key)) {
            return _.propertyOf(settings)(key);
          } else {
            return $q.reject("The key is not defined in the settings.");
          }
        });
    }

    function saveSetting(key, value) {
      return getSetting()
        .then(function(settings) {
          settings[key] = value;
          var data = {};
          var kvp = {};

          kvp[key] = value;

          data["settings-v1.0"] = settings;

          chrome.storage.sync.set(data, function(e) {
            if (chrome.runtime.lastError) {
              $q.reject(chrome.runtime.lastError);
            } else {
              $rootScope.$broadcast('dim-settings-updated', kvp);
              return true;
            }
          });
        });
    }
  }
})();
<|endoftext|>


<commit_before>'use strict';

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

const Exchange = require ('./base/Exchange');
const Precise = require ('./base/Precise');

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

module.exports = class bl3p extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bl3p',
            'name': 'BL3P',
            'countries': [ 'NL' ], // Netherlands
            'rateLimit': 1000,
            'version': '1',
            'comment': 'An exchange market by BitonicNL',
            'has': {
                'CORS': undefined,
                'spot': true,
                'margin': false,
                'swap': false,
                'future': false,
                'option': false,
                'cancelOrder': true,
                'createOrder': true,
                'createReduceOnlyOrder': false,
                'fetchBalance': true,
                'fetchBorrowRate': false,
                'fetchBorrowRateHistory': false,
                'fetchBorrowRates': false,
                'fetchBorrowRatesPerSymbol': false,
                'fetchFundingHistory': false,
                'fetchFundingRate': false,
                'fetchFundingRateHistory': false,
                'fetchFundingRates': false,
                'fetchIndexOHLCV': false,
                'fetchIsolatedPositions': false,
                'fetchLeverage': false,
                'fetchMarkOHLCV': false,
                'fetchOrderBook': true,
                'fetchPositions': false,
                'fetchPositionsRisk': false,
                'fetchPremiumIndexOHLCV': false,
                'fetchTicker': true,
                'fetchTrades': true,
                'reduceMargin': false,
                'setLeverage': false,
                'setMarginMode': false,
                'setPositionMode': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg',
                'api': 'https://api.bl3p.eu',
                'www': 'https://bl3p.eu', // 'https://bitonic.nl'
                'doc': [
                    'https://github.com/BitonicNL/bl3p-api/tree/master/docs',
                    'https://bl3p.eu/api',
                    'https://bitonic.nl/en/api',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        '{market}/ticker',
                        '{market}/orderbook',
                        '{market}/trades',
                    ],
                },
                'private': {
                    'post': [
                        '{market}/money/depth/full',
                        '{market}/money/order/add',
                        '{market}/money/order/cancel',
                        '{market}/money/order/result',
                        '{market}/money/orders',
                        '{market}/money/orders/history',
                        '{market}/money/trades/fetch',
                        'GENMKT/money/info',
                        'GENMKT/money/deposit_address',
                        'GENMKT/money/new_deposit_address',
                        'GENMKT/money/wallet/history',
                        'GENMKT/money/withdraw',
                    ],
                },
            },
            'markets': {
                'BTC/EUR': { 'id': 'BTCEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025, 'type': 'spot', 'spot': true },
                'LTC/EUR': { 'id': 'LTCEUR', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'LTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025, 'type': 'spot', 'spot': true },
            },
        });
    }

    parseBalance (response) {
        const data = this.safeValue (response, 'data', {});
        const wallets = this.safeValue (data, 'wallets');
        const result = { 'info': data };
        const codes = Object.keys (this.currencies);
        for (let i = 0; i < codes.length; i++) {
            const code = codes[i];
            const currency = this.currency (code);
            const currencyId = currency['id'];
            const wallet = this.safeValue (wallets, currencyId, {});
            const available = this.safeValue (wallet, 'available', {});
            const balance = this.safeValue (wallet, 'balance', {});
            const account = this.account ();
            account['free'] = this.safeString (available, 'value');
            account['total'] = this.safeString (balance, 'value');
            result[code] = account;
        }
        return this.safeBalance (result);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const response = await this.privatePostGENMKTMoneyInfo (params);
        return this.parseBalance (response);
    }

    parseBidAsk (bidask, priceKey = 0, amountKey = 1) {
        const price = this.safeNumber (bidask, priceKey);
        const size = this.safeNumber (bidask, amountKey);
        return [
            price / 100000.0,
            size / 100000000.0,
        ];
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        const market = this.market (symbol);
        const request = {
            'market': market['id'],
        };
        const response = await this.publicGetMarketOrderbook (this.extend (request, params));
        const orderbook = this.safeValue (response, 'data');
        return this.parseOrderBook (orderbook, symbol, undefined, 'bids', 'asks', 'price_int', 'amount_int');
    }

    async fetchTicker (symbol, params = {}) {
        const request = {
            'market': this.marketId (symbol),
        };
        const ticker = await this.publicGetMarketTicker (this.extend (request, params));
        const timestamp = this.safeTimestamp (ticker, 'timestamp');
        const last = this.safeNumber (ticker, 'last');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeNumber (ticker, 'high'),
            'low': this.safeNumber (ticker, 'low'),
            'bid': this.safeNumber (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeNumber (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeNumber (ticker['volume'], '24h'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    parseTrade (trade, market = undefined) {
        const id = this.safeString (trade, 'trade_id');
        const timestamp = this.safeInteger (trade, 'date');
        let priceString = this.safeString (trade, 'price_int');
        priceString = Precise.stringDiv (priceString, '100000');
        let amountString = this.safeString (trade, 'amount_int');
        amountString = Precise.stringDiv (amountString, '100000000');
        const price = this.parseNumber (priceString);
        const amount = this.parseNumber (amountString);
        const cost = this.parseNumber (Precise.stringMul (priceString, amountString));
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'type': undefined,
            'side': undefined,
            'order': undefined,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
        };
    }

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

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        const market = this.market (symbol);
        const order = {
            'market': market['id'],
            'amount_int': parseInt (amount * 100000000),
            'fee_currency': market['quote'],
            'type': (side === 'buy') ? 'bid' : 'ask',
        };
        if (type === 'limit') {
            order['price_int'] = parseInt (price * 100000.0);
        }
        const response = await this.privatePostMarketMoneyOrderAdd (this.extend (order, params));
        const orderId = this.safeString (response['data'], 'order_id');
        return {
            'info': response,
            'id': orderId,
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        const request = {
            'order_id': id,
        };
        return await this.privatePostMarketMoneyOrderCancel (this.extend (request, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const request = this.implodeParams (path, params);
        let url = this.urls['api'] + '/' + this.version + '/' + request;
        const query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            const nonce = this.nonce ();
            body = this.urlencode (this.extend ({ 'nonce': nonce }, query));
            const secret = this.base64ToBinary (this.secret);
            // eslint-disable-next-line quotes
            const auth = request + "\0" + body;
            const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64');
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Rest-Key': this.apiKey,
                'Rest-Sign': signature,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }
};
<commit_msg>bl3p: use string in safeTicker ccxt/ccxt#11379<commit_after>'use strict';

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

const Exchange = require ('./base/Exchange');
const Precise = require ('./base/Precise');

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

module.exports = class bl3p extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bl3p',
            'name': 'BL3P',
            'countries': [ 'NL' ], // Netherlands
            'rateLimit': 1000,
            'version': '1',
            'comment': 'An exchange market by BitonicNL',
            'has': {
                'CORS': undefined,
                'spot': true,
                'margin': false,
                'swap': false,
                'future': false,
                'option': false,
                'cancelOrder': true,
                'createOrder': true,
                'createReduceOnlyOrder': false,
                'fetchBalance': true,
                'fetchBorrowRate': false,
                'fetchBorrowRateHistory': false,
                'fetchBorrowRates': false,
                'fetchBorrowRatesPerSymbol': false,
                'fetchFundingHistory': false,
                'fetchFundingRate': false,
                'fetchFundingRateHistory': false,
                'fetchFundingRates': false,
                'fetchIndexOHLCV': false,
                'fetchIsolatedPositions': false,
                'fetchLeverage': false,
                'fetchMarkOHLCV': false,
                'fetchOrderBook': true,
                'fetchPositions': false,
                'fetchPositionsRisk': false,
                'fetchPremiumIndexOHLCV': false,
                'fetchTicker': true,
                'fetchTrades': true,
                'reduceMargin': false,
                'setLeverage': false,
                'setMarginMode': false,
                'setPositionMode': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg',
                'api': 'https://api.bl3p.eu',
                'www': 'https://bl3p.eu', // 'https://bitonic.nl'
                'doc': [
                    'https://github.com/BitonicNL/bl3p-api/tree/master/docs',
                    'https://bl3p.eu/api',
                    'https://bitonic.nl/en/api',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        '{market}/ticker',
                        '{market}/orderbook',
                        '{market}/trades',
                    ],
                },
                'private': {
                    'post': [
                        '{market}/money/depth/full',
                        '{market}/money/order/add',
                        '{market}/money/order/cancel',
                        '{market}/money/order/result',
                        '{market}/money/orders',
                        '{market}/money/orders/history',
                        '{market}/money/trades/fetch',
                        'GENMKT/money/info',
                        'GENMKT/money/deposit_address',
                        'GENMKT/money/new_deposit_address',
                        'GENMKT/money/wallet/history',
                        'GENMKT/money/withdraw',
                    ],
                },
            },
            'markets': {
                'BTC/EUR': { 'id': 'BTCEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025, 'type': 'spot', 'spot': true },
                'LTC/EUR': { 'id': 'LTCEUR', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'LTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025, 'type': 'spot', 'spot': true },
            },
        });
    }

    parseBalance (response) {
        const data = this.safeValue (response, 'data', {});
        const wallets = this.safeValue (data, 'wallets');
        const result = { 'info': data };
        const codes = Object.keys (this.currencies);
        for (let i = 0; i < codes.length; i++) {
            const code = codes[i];
            const currency = this.currency (code);
            const currencyId = currency['id'];
            const wallet = this.safeValue (wallets, currencyId, {});
            const available = this.safeValue (wallet, 'available', {});
            const balance = this.safeValue (wallet, 'balance', {});
            const account = this.account ();
            account['free'] = this.safeString (available, 'value');
            account['total'] = this.safeString (balance, 'value');
            result[code] = account;
        }
        return this.safeBalance (result);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const response = await this.privatePostGENMKTMoneyInfo (params);
        return this.parseBalance (response);
    }

    parseBidAsk (bidask, priceKey = 0, amountKey = 1) {
        const price = this.safeNumber (bidask, priceKey);
        const size = this.safeNumber (bidask, amountKey);
        return [
            price / 100000.0,
            size / 100000000.0,
        ];
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        const market = this.market (symbol);
        const request = {
            'market': market['id'],
        };
        const response = await this.publicGetMarketOrderbook (this.extend (request, params));
        const orderbook = this.safeValue (response, 'data');
        return this.parseOrderBook (orderbook, symbol, undefined, 'bids', 'asks', 'price_int', 'amount_int');
    }

    parseTicker (ticker, market = undefined) {
        //
        // {
        //     "currency":"BTC",
        //     "last":32654.55595,
        //     "bid":32552.3642,
        //     "ask":32703.58231,
        //     "high":33500,
        //     "low":31943,
        //     "timestamp":1643372789,
        //     "volume":{
        //         "24h":2.27372413,
        //         "30d":320.79375456
        //     }
        // }
        //
        const symbol = this.safeSymbol (undefined, market);
        const timestamp = this.safeTimestamp (ticker, 'timestamp');
        const last = this.safeString (ticker, 'last');
        return this.safeTicker ({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeString (ticker, 'high'),
            'low': this.safeString (ticker, 'low'),
            'bid': this.safeString (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeString (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeString (this.safeValue (ticker, 'volume'), '24h'),
            'quoteVolume': undefined,
            'info': ticker,
        }, market, false);
    }

    async fetchTicker (symbol, params = {}) {
        const market = this.market (symbol);
        const request = {
            'market': market['id'],
        };
        const ticker = await this.publicGetMarketTicker (this.extend (request, params));
        //
        // {
        //     "currency":"BTC",
        //     "last":32654.55595,
        //     "bid":32552.3642,
        //     "ask":32703.58231,
        //     "high":33500,
        //     "low":31943,
        //     "timestamp":1643372789,
        //     "volume":{
        //         "24h":2.27372413,
        //         "30d":320.79375456
        //     }
        // }
        //
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market = undefined) {
        const id = this.safeString (trade, 'trade_id');
        const timestamp = this.safeInteger (trade, 'date');
        let priceString = this.safeString (trade, 'price_int');
        priceString = Precise.stringDiv (priceString, '100000');
        let amountString = this.safeString (trade, 'amount_int');
        amountString = Precise.stringDiv (amountString, '100000000');
        const price = this.parseNumber (priceString);
        const amount = this.parseNumber (amountString);
        const cost = this.parseNumber (Precise.stringMul (priceString, amountString));
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'type': undefined,
            'side': undefined,
            'order': undefined,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
        };
    }

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

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        const market = this.market (symbol);
        const order = {
            'market': market['id'],
            'amount_int': parseInt (amount * 100000000),
            'fee_currency': market['quote'],
            'type': (side === 'buy') ? 'bid' : 'ask',
        };
        if (type === 'limit') {
            order['price_int'] = parseInt (price * 100000.0);
        }
        const response = await this.privatePostMarketMoneyOrderAdd (this.extend (order, params));
        const orderId = this.safeString (response['data'], 'order_id');
        return {
            'info': response,
            'id': orderId,
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        const request = {
            'order_id': id,
        };
        return await this.privatePostMarketMoneyOrderCancel (this.extend (request, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const request = this.implodeParams (path, params);
        let url = this.urls['api'] + '/' + this.version + '/' + request;
        const query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            const nonce = this.nonce ();
            body = this.urlencode (this.extend ({ 'nonce': nonce }, query));
            const secret = this.base64ToBinary (this.secret);
            // eslint-disable-next-line quotes
            const auth = request + "\0" + body;
            const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64');
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Rest-Key': this.apiKey,
                'Rest-Sign': signature,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }
};
<|endoftext|>


<commit_before>/*
 * The terms and conditions of the Simplified BSD License apply to this source code.
 * Copyright (c) 2016, Jouke Witteveen
 */

L.Control.Distance = L.Control.extend({
	options: {
		// Redraw when a track point is being dragged
		updateWhileDragging: true,

		// Size of the circle markers defining the path
		pointRadius: L.Browser.mobile && L.Browser.touch ? 10 : 4,

		// Reflect the path in the URL
		useHash: true,

		// Class name for the button on the map
		iconClass: 'fa fa-arrows-h',

		// Position of the control
		position: 'bottomleft',

		// Internationalization
		strings: {
			measureDistance: 'Measure distance',
			totalDistance: 'Total distance',
			clickToDraw: 'Click on the map to trace a path you want to measure',
			clickToRemove: 'Click to remove'
		}
	},

	onAdd: function (map) {
		var container = this._container = L.DomUtil.create('div', 'leaflet-control-distance leaflet-bar'),
		    link = L.DomUtil.create('a', 'distance-button', container);
		link.href = '#';
		link.title = this.options.strings.measureDistance;
		L.DomUtil.create('span', this.options.iconClass, link);
		this._distance = L.DomUtil.create('span', 'distance-measurement', container);
		this._layers = L.layerGroup();
		this._points = [];
		this._path = L.polyline([], {
			color: 'black',
			weight: '2',
			className: 'distance-path',
			interactive: false,
			pane: 'markerPane'
		});
		this._markers = [];
		L.DomEvent.disableClickPropagation(container)
		          .disableScrollPropagation(container)
		          .on(link, 'click', L.DomEvent.stop)
		          .on(link, 'click', this._toggle, this);
		if (this.options.useHash) {
			this._updateHash = L.Util.throttle(this._writeHash, 1000, this);
			L.DomEvent.on(window, 'hashchange', this._readHash, this);
			this._readHash();
		} else this._updateHash = function () {};
		return container;
	},

	onRemove: function (map) {
		this._disable();
	},

	// Set the correct text on the control and update the markers along the path
	updateDistance: function () {
		this._updateHash();
		if (!this._points.length) {
			this._distance.textContent = this._active ? this.options.strings.clickToDraw : '';
			return;
		}
		var latlngs = this._path.getLatLngs(),
		    distances = this._accumulatedDistances(latlngs),
		    distance = distances[distances.length - 1],
		    distanceText;
		if (distance < 1)
			distanceText = (distance * 1000).toFixed() + ' m';
		else
			distanceText = distance.toFixed(distance < 10 ? 2 : distance < 100 ? 1 : 0) + ' km';
		this._distance.textContent = this.options.strings.totalDistance + ': ' + distanceText;
		this._updateMarkers(
			distance <=  20 ?  1 :
			distance <=  50 ?  2 :
			distance <= 100 ?  5 :
			distance <= 200 ? 10 :
			distance <= 500 ? 20 :
			                  50,
			latlngs, distances
		);
	},

	_enable: function () {
		if (this.options.useHash) L.DomEvent.off(window, 'hashchange', this._readHash, this);
		this._layers.addTo(this._map);
		this._layers.addLayer(this._path);
		this._path.bringToBack();
		this._map.on('click', this._addPoint, this);
		L.DomUtil.addClass(this._map.getContainer(), 'leaflet-crosshair leaflet-drag');
		L.DomUtil.addClass(this.getContainer(), 'distance-active');
		this._active = true;
		this.updateDistance();
	},

	_disable: function () {
		if (this.options.useHash) L.DomEvent.on(window, 'hashchange', this._readHash, this);
		this._map.off('click', this._addPoint, this);
		this._layers.remove();
		L.DomUtil.removeClass(this._map.getContainer(), 'leaflet-crosshair');
		if (!this._map.options.dragging)
			L.DomUtil.removeClass(this._map.getContainer(), 'leaflet-drag');
		L.DomUtil.removeClass(this.getContainer(), 'distance-active');
		this._layers.clearLayers();
		this._points = [];
		this._path.setLatLngs([]);
		this._markers = [];
		this._active = false;
		this.updateDistance();
	},

	_toggle: function () {
		if (this._active) this._disable();
		else this._enable();
	},

	_addPoint: function (e) {
		if (!e.latlng) return;
		var point = L.circleMarker.draggable(e.latlng, {
			radius: this.options.pointRadius,
			weight: 2,
			color: 'black',
			fillColor: 'white',
			fillOpacity: 1,
			className: 'distance-point',
			tooltip: this.options.strings.clickToRemove,
			pane: 'markerPane'
		})
			.on('click', this._deletePoint, this)
			.on('click', L.DomEvent.stopPropagation)
			.on('dragend', this._updatePath, this);
		if (this.options.updateWhileDragging)
			point.on('drag', this._updatePath, this);
		this._points.push(point);
		this._layers.addLayer(point);
		this._path.addLatLng(e.latlng);
		if (e.type !== 'import') this.updateDistance();
	},

	_deletePoint: function (e) {
		if (e.target._moved) return;
		for (var i = 0; i < this._points.length; i++) {
			if (this._points[i] === e.target) {
				this._layers.removeLayer(e.target);
				this._points.splice(i, 1);
				this._updatePath();
				break;
			}
		}
	},

	_updateMarkers: function (spacing, latlngs, distances) {
		var marker, fromEnd, latlng;
		this._points[0].setStyle({
			radius: .75 * this.options.pointRadius,
			fillColor: 'black'
		});
		while (marker = this._markers.pop())
			this._layers.removeLayer(marker);
		for (var i = 1, next = spacing, j, begin, end, step; i < distances.length; i++) {
			while (distances[i] > next) {
				if (j !== i) {
					begin = this._map.latLngToLayerPoint(latlngs[i - 1]);
					end = this._map.latLngToLayerPoint(latlngs[i]);
					step = end.subtract(begin);
					j = i;
				}
				fromEnd = (distances[i] - next) / (distances[i] - distances[i - 1]);
				latlng = this._map.layerPointToLatLng(end.subtract(step.multiplyBy(fromEnd)));
				marker = L.marker.distance(latlng, {
					html: next.toFixed(),
					className: 'distance-marker'
				});
				this._markers.push(marker);
				this._layers.addLayer(marker);
				next += spacing;
			}
		}
	},

	_updatePath: function () {
		var latlngs = [];
		for (var i = 0; i < this._points.length; i++) {
			latlngs.push(this._points[i].getLatLng());
		}
		this._path.setLatLngs(latlngs);
		this.updateDistance();
	},

	_accumulatedDistances: function (latlngs) {
		var total = 0,
		    distances = [0];
		for (var i = 0, n = latlngs.length - 1; i < n; i++) {
			total += latlngs[i].distanceTo(latlngs[i + 1]) / 1000;
			distances.push(total);
		}
		return distances;
	},

	_importPath: function (hash) {
		var w = 4, res = Base64.resolution(w) - 1;
		if (!Base64.test(hash, 2 * w)) return false;
		this._points = [];
		for (var i = 0, lat, lng; i < hash.length; i += 2 * w) {
			lat = Base64.toInt(hash.substr(i, w)) / res * 180 - 90;
			lng = Base64.toInt(hash.substr(i + w, w)) / res * 360 - 180;
			this._addPoint({
				type: 'import',
				target: this,
				latlng: new L.LatLng(lat, lng)
			});
		}
		return true;
	},

	_exportPath: function () {
		var w = 4, res = Base64.resolution(w) - 1, hash = '';
		for (var i = 0, latlng; i < this._points.length; i++) {
			latlng = this._points[i].getLatLng();
			hash += Base64.fromInt(Math.round((latlng.lat + 90) / 180 * res), w)
			        + Base64.fromInt(Math.round((latlng.lng + 180) / 360 * res), w);
		}
		return hash;
	},

	_readHash: function () {
		var hash = /[#&]path=([^&]*)/.exec(location.hash);
		if (hash) if (this._importPath(hash[1])) this._enable();
	},

	_writeHash: function () {
		var hash = location.hash.replace(/[#&]path=[^&]*/, '');
		if (this._active) hash += '&path=' + this._exportPath();
		window.history.replaceState(window.history.state, document.title, '#' + hash.substr(1));
	}
});

L.control.distance = function (options) {
	return new L.Control.Distance(options);
};

L.CircleMarker.Draggable = L.CircleMarker.extend({
	_dragStart: function (e) {
		this._moved = false;
		this._map
			.on('mousemove', this._drag, this)
			.once('mouseup', this._dragEnd, this);
		this._map.dragging.disable();
		L.DomUtil.addClass(this._map.getContainer(), 'leaflet-grab');
		L.DomUtil.addClass(document.body, 'leaflet-dragging');
		this.unbindTooltip();
		this.fire('dragstart', e);
	},

	_drag: function (e) {
		this.setLatLng(e.latlng);
		this._moved = true;
		L.DomEvent.preventDefault(e);
		this.fire('drag');
	},

	_dragEnd: function (e) {
		this._map
			.off('mousemove', this._drag, this);
		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
		if (this._map.options.dragging)
			this._map.dragging.enable();
		else
			L.DomUtil.removeClass(map.getContainer(), 'leaflet-grab');
		if (this.options.tooltip)
			this.bindTooltip(this.options.tooltip);
		this.fire('dragend', e);
	}
});
L.CircleMarker.Draggable.addInitHook('on', 'mousedown', L.CircleMarker.Draggable.prototype._dragStart);
L.CircleMarker.Draggable.addInitHook('once', 'mouseout', function () {
	if (this.options.tooltip && typeof this._moved === 'undefined')
		this.bindTooltip(this.options.tooltip);
});

L.circleMarker.draggable = function (latlng, options) {
	return new L.CircleMarker.Draggable (latlng, options);
}

L.Marker.Distance = L.Marker.extend({
	initialize: function (latlng, options) {
		L.Marker.prototype.initialize.call(this, latlng, {
			icon: L.divIcon(L.extend({ iconSize: null }, options)),
			keyboard: false,
			interactive: false
		});
	},

	onAdd: function (map) {
		L.Marker.prototype.onAdd.call(this, map);
		this._icon.style.zIndex = L.DomUtil.getStyle(this.getPane(), 'z-index');
	},

	_updateZIndex: function () {}
});

L.marker.distance = function (latlng, options) {
	return new L.Marker.Distance(latlng, options);
};

Base64 = (function () {
	var chars =
	//   0       8       16      24      32      40      48      56     63
	//   v       v       v       v       v       v       v       v      v
	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~_",
	    values = {};
	for (var i = 0; i < chars.length; i++) {
		values[chars[i]] = i;
	}
	return {
		resolution: function (w) {
			return 1 << (6 * w);
		},

		test: function (s, w) {
			if (!w) w = 1;
			return new RegExp('^(?:[' + chars + ']{' + w + '})*$').test(s);
		},

		fromInt: function (n, w) {
			var s = '';
			while (w--) {
				s = chars[n & 0x3f] + s;
				n >>>= 6;
			}
			return s;
		},

		toInt: function (s) {
			var n = 0, s = s.split('');
			for (var i = 0; i < s.length; i++)
				n = (n << 6) + values[s[i]];
			return n;
		}
	};
})();
<commit_msg>Only fire drag when actually moving<commit_after>/*
 * The terms and conditions of the Simplified BSD License apply to this source code.
 * Copyright (c) 2016, Jouke Witteveen
 */

L.Control.Distance = L.Control.extend({
	options: {
		// Redraw when a track point is being dragged
		updateWhileDragging: true,

		// Size of the circle markers defining the path
		pointRadius: L.Browser.mobile && L.Browser.touch ? 10 : 4,

		// Reflect the path in the URL
		useHash: true,

		// Class name for the button on the map
		iconClass: 'fa fa-arrows-h',

		// Position of the control
		position: 'bottomleft',

		// Internationalization
		strings: {
			measureDistance: 'Measure distance',
			totalDistance: 'Total distance',
			clickToDraw: 'Click on the map to trace a path you want to measure',
			clickToRemove: 'Click to remove'
		}
	},

	onAdd: function (map) {
		var container = this._container = L.DomUtil.create('div', 'leaflet-control-distance leaflet-bar'),
		    link = L.DomUtil.create('a', 'distance-button', container);
		link.href = '#';
		link.title = this.options.strings.measureDistance;
		L.DomUtil.create('span', this.options.iconClass, link);
		this._distance = L.DomUtil.create('span', 'distance-measurement', container);
		this._layers = L.layerGroup();
		this._points = [];
		this._path = L.polyline([], {
			color: 'black',
			weight: '2',
			className: 'distance-path',
			interactive: false,
			pane: 'markerPane'
		});
		this._markers = [];
		L.DomEvent.disableClickPropagation(container)
		          .disableScrollPropagation(container)
		          .on(link, 'click', L.DomEvent.stop)
		          .on(link, 'click', this._toggle, this);
		if (this.options.useHash) {
			this._updateHash = L.Util.throttle(this._writeHash, 1000, this);
			L.DomEvent.on(window, 'hashchange', this._readHash, this);
			this._readHash();
		} else this._updateHash = function () {};
		return container;
	},

	onRemove: function (map) {
		this._disable();
	},

	// Set the correct text on the control and update the markers along the path
	updateDistance: function () {
		this._updateHash();
		if (!this._points.length) {
			this._distance.textContent = this._active ? this.options.strings.clickToDraw : '';
			return;
		}
		var latlngs = this._path.getLatLngs(),
		    distances = this._accumulatedDistances(latlngs),
		    distance = distances[distances.length - 1],
		    distanceText;
		if (distance < 1)
			distanceText = (distance * 1000).toFixed() + ' m';
		else
			distanceText = distance.toFixed(distance < 10 ? 2 : distance < 100 ? 1 : 0) + ' km';
		this._distance.textContent = this.options.strings.totalDistance + ': ' + distanceText;
		this._updateMarkers(
			distance <=  20 ?  1 :
			distance <=  50 ?  2 :
			distance <= 100 ?  5 :
			distance <= 200 ? 10 :
			distance <= 500 ? 20 :
			                  50,
			latlngs, distances
		);
	},

	_enable: function () {
		if (this.options.useHash)
			L.DomEvent.off(window, 'hashchange', this._readHash, this);
		this._layers.addTo(this._map);
		this._layers.addLayer(this._path);
		this._path.bringToBack();
		this._map.on('click', this._addPoint, this);
		L.DomUtil.addClass(this._map.getContainer(), 'leaflet-crosshair leaflet-drag');
		L.DomUtil.addClass(this.getContainer(), 'distance-active');
		this._active = true;
		this.updateDistance();
	},

	_disable: function () {
		if (this.options.useHash)
			L.DomEvent.on(window, 'hashchange', this._readHash, this);
		this._map.off('click', this._addPoint, this);
		this._layers.remove();
		L.DomUtil.removeClass(this._map.getContainer(), 'leaflet-crosshair');
		if (!this._map.options.dragging)
			L.DomUtil.removeClass(this._map.getContainer(), 'leaflet-drag');
		L.DomUtil.removeClass(this.getContainer(), 'distance-active');
		this._layers.clearLayers();
		this._points = [];
		this._path.setLatLngs([]);
		this._markers = [];
		this._active = false;
		this.updateDistance();
	},

	_toggle: function () {
		if (this._active) this._disable();
		else this._enable();
	},

	_addPoint: function (e) {
		if (!e.latlng) return;
		var point = L.circleMarker.draggable(e.latlng, {
			radius: this.options.pointRadius,
			weight: 2,
			color: 'black',
			fillColor: 'white',
			fillOpacity: 1,
			className: 'distance-point',
			tooltip: this.options.strings.clickToRemove,
			pane: 'markerPane'
		})
			.on('click', L.DomEvent.stopPropagation)
			.on('dragend', this._updatePath, this);
		setTimeout(L.bind(point.on, point, 'click', this._deletePoint, this), 500);
		if (this.options.updateWhileDragging)
			point.on('drag', this._updatePath, this);
		this._points.push(point);
		this._layers.addLayer(point);
		this._path.addLatLng(e.latlng);
		if (e.type !== 'import') this.updateDistance();
	},

	_deletePoint: function (e) {
		if (e.target._moved) return;
		for (var i = 0; i < this._points.length; i++) {
			if (this._points[i] === e.target) {
				this._layers.removeLayer(e.target);
				this._points.splice(i, 1);
				this._updatePath();
				break;
			}
		}
	},

	_updateMarkers: function (spacing, latlngs, distances) {
		var marker, fromEnd, latlng;
		this._points[0].setStyle({
			radius: .75 * this.options.pointRadius,
			fillColor: 'black'
		});
		while (marker = this._markers.pop())
			this._layers.removeLayer(marker);
		for (var i = 1, next = spacing, j, begin, end, step; i < distances.length; i++) {
			while (distances[i] > next) {
				if (j !== i) {
					begin = this._map.latLngToLayerPoint(latlngs[i - 1]);
					end = this._map.latLngToLayerPoint(latlngs[i]);
					step = end.subtract(begin);
					j = i;
				}
				fromEnd = (distances[i] - next) / (distances[i] - distances[i - 1]);
				latlng = this._map.layerPointToLatLng(end.subtract(step.multiplyBy(fromEnd)));
				marker = L.marker.distance(latlng, {
					html: next.toFixed(),
					className: 'distance-marker'
				});
				this._markers.push(marker);
				this._layers.addLayer(marker);
				next += spacing;
			}
		}
	},

	_updatePath: function () {
		var latlngs = [];
		for (var i = 0; i < this._points.length; i++) {
			latlngs.push(this._points[i].getLatLng());
		}
		this._path.setLatLngs(latlngs);
		this.updateDistance();
	},

	_accumulatedDistances: function (latlngs) {
		var total = 0,
		    distances = [0];
		for (var i = 0, n = latlngs.length - 1; i < n; i++) {
			total += latlngs[i].distanceTo(latlngs[i + 1]) / 1000;
			distances.push(total);
		}
		return distances;
	},

	_importPath: function (hash) {
		var w = 4, res = Base64.resolution(w) - 1;
		if (!Base64.test(hash, 2 * w)) return false;
		this._points = [];
		for (var i = 0, lat, lng; i < hash.length; i += 2 * w) {
			lat = Base64.toInt(hash.substr(i, w)) / res * 180 - 90;
			lng = Base64.toInt(hash.substr(i + w, w)) / res * 360 - 180;
			this._addPoint({
				type: 'import',
				target: this,
				latlng: new L.LatLng(lat, lng)
			});
		}
		return true;
	},

	_exportPath: function () {
		var w = 4, res = Base64.resolution(w) - 1, hash = '';
		for (var i = 0, latlng; i < this._points.length; i++) {
			latlng = this._points[i].getLatLng();
			hash += Base64.fromInt(Math.round((latlng.lat + 90) / 180 * res), w)
			        + Base64.fromInt(Math.round((latlng.lng + 180) / 360 * res), w);
		}
		return hash;
	},

	_readHash: function () {
		var hash = /[#&]path=([^&]*)/.exec(location.hash);
		if (hash && this._importPath(hash[1])) this._enable();
	},

	_writeHash: function () {
		var hash = location.hash.replace(/[#&]path=[^&]*/, '');
		if (this._active) hash += '&path=' + this._exportPath();
		window.history.replaceState(window.history.state, document.title, '#' + hash.substr(1));
	}
});

L.control.distance = function (options) {
	return new L.Control.Distance(options);
};

L.CircleMarker.Draggable = L.CircleMarker.extend({
	_dragStart: function (e) {
		this._moved = false;
		this._map
			.on('mousemove', this._drag, this)
			.once('mouseup', this._dragEnd, this);
		this._map.dragging.disable();
		L.DomUtil.addClass(this._map.getContainer(), 'leaflet-grab');
		L.DomUtil.addClass(document.body, 'leaflet-dragging');
		this.unbindTooltip();
		this.fire('dragstart', e);
	},

	_drag: function (e) {
		if (!e.latlng || this.getLatLng().equals(e.latlng)) return;
		this.setLatLng(e.latlng);
		this._moved = true;
		this.fire('drag');
	},

	_dragEnd: function (e) {
		this._map
			.off('mousemove', this._drag, this);
		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
		if (this._map.options.dragging)
			this._map.dragging.enable();
		else
			L.DomUtil.removeClass(map.getContainer(), 'leaflet-grab');
		if (this.options.tooltip)
			setTimeout(L.bind(this.bindTooltip, this, this.options.tooltip), 500);
		this.fire('dragend', e);
	}
});
L.CircleMarker.Draggable.addInitHook('on', 'mousedown', L.CircleMarker.Draggable.prototype._dragStart);
L.CircleMarker.Draggable.addInitHook('once', 'mouseout', function () {
	if (this.options.tooltip && typeof this._moved === 'undefined')
		this.bindTooltip(this.options.tooltip);
});

L.circleMarker.draggable = function (latlng, options) {
	return new L.CircleMarker.Draggable (latlng, options);
}

L.Marker.Distance = L.Marker.extend({
	initialize: function (latlng, options) {
		L.Marker.prototype.initialize.call(this, latlng, {
			icon: L.divIcon(L.extend({ iconSize: null }, options)),
			keyboard: false,
			interactive: false
		});
	},

	onAdd: function (map) {
		L.Marker.prototype.onAdd.call(this, map);
		this._icon.style.zIndex = L.DomUtil.getStyle(this.getPane(), 'z-index');
	},

	_updateZIndex: function () {}
});

L.marker.distance = function (latlng, options) {
	return new L.Marker.Distance(latlng, options);
};

Base64 = (function () {
	var chars =
	//   0       8       16      24      32      40      48      56     63
	//   v       v       v       v       v       v       v       v      v
	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~_",
	    values = {};
	for (var i = 0; i < chars.length; i++) {
		values[chars[i]] = i;
	}
	return {
		resolution: function (w) {
			return 1 << (6 * w);
		},

		test: function (s, w) {
			if (!w) w = 1;
			return new RegExp('^(?:[' + chars + ']{' + w + '})*$').test(s);
		},

		fromInt: function (n, w) {
			var s = '';
			while (w--) {
				s = chars[n & 0x3f] + s;
				n >>>= 6;
			}
			return s;
		},

		toInt: function (s) {
			var n = 0, s = s.split('');
			for (var i = 0; i < s.length; i++)
				n = (n << 6) + values[s[i]];
			return n;
		}
	};
})();
<|endoftext|>