<commit_before>var Upvote = (function (window, document) {

	that = this;

	// endEvent = hasTouch ? 'touchend' : 'mouseup',

	Upvote = function (opts) {

		that = this;

		this.current_category = 'us';

		// ---------------------------------------------------------
		// Muench launch
		// ---------------------------------------------------------

		// Options from user
		for (i in opts) this.options[i] = opts[i];

		// ---------------------------------------------------------
		// Skimmin launch
		// ---------------------------------------------------------

        	$('.upvote-container').on('click', this.fn_tap_upvote.bind(this));
        	$('.category-container').on('click', this.fn_change_category.bind(this));

		window.addEventListener('load', function() {
		    FastClick.attach(document.body);
		}, false);

		this.fn_hide_rendering_icons();
		this.fn_loading();
       		this.fn_reveal_content();

    	};

	Upvote.prototype = {

		// ------------------------------------------------------
		// Skimmin Core Functions
		// ------------------------------------------------------

        	// primarily used to hide slow rendering
        	fn_loading: function() {

            	that = this;

            	$('#L').fadeIn(0);
            	$('#L').addClass('animated zoomIn');

            	setTimeout(function() {
            		$('#O').fadeIn(0);
            		$('#O').addClass('animated zoomIn');
            	}, 65);

            	setTimeout(function() {
            		$('#A').fadeIn(0);
            		$('#A').addClass('animated zoomIn');
            	}, 130);

            	setTimeout(function() {
            		$('#D').fadeIn(0);
            		$('#D').addClass('animated zoomIn');
            	}, 195);

            	setTimeout(function() {
            		$('#I').fadeIn(0);
            		$('#I').addClass('animated zoomIn');
            	}, 260);

            	setTimeout(function() {
            		$('#N').fadeIn(0);
            		$('#N').addClass('animated zoomIn');
            	}, 325);

            	setTimeout(function() {
            		$('#G').fadeIn(0);
            		$('#G').addClass('animated zoomIn');
            	}, 390);

            	/*setTimeout(function() {
            		$('#loading-container').addClass('animated pulse');
            	}, 1500);*/

            	/*setTimeout(function() {
            		$('#L').removeClass('animated zoomIn');
            		$('#O').removeClass('animated zoomIn');
            		$('#A').removeClass('animated zoomIn');
            		$('#D').removeClass('animated zoomIn');
            		$('#I').removeClass('animated zoomIn');
            		$('#N').removeClass('animated zoomIn');
            		$('#G').removeClass('animated zoomIn');
            	}, 1500);*/

            	int_loading_count = 0;
            	arr_colors = Array('rgba(52, 152, 219, 1)', 'rgba(255, 140, 0, 1)', 'rgba(231, 76, 60, 1)', 'rgba(0, 128, 0, .9)', 'rgba(0, 154, 136, .9)', 'rgba(164, 121, 228, 1)');

            	// bool_kill_next_loop = false;

        		// $('#loading-circle').addClass('animated pulse');

        	pulsing_circle_interval = setInterval(function() {

        			/*$('#L').fadeOut(300);
        			$('#O').fadeOut(300);
        			$('#A').fadeOut(300);
        			$('#D').fadeOut(300);
        			$('#I').fadeOut(300);
        			$('#N').fadeOut(300);
        			$('#G').fadeOut(300);*/

        			if (int_loading_count == 0 || int_loading_count == 1 || int_loading_count == 2 || int_loading_count == 3) {

        				/*$('#L').fadeIn(150);

            				setTimeout(function() {
            					$('#O').fadeIn(150);
            				}, 75);

            				setTimeout(function() {
            					$('#A').fadeIn(150);
            				}, 150);

            				setTimeout(function() {
            					$('#D').fadeIn(150);
            				}, 225);

            				setTimeout(function() {
            					$('#I').fadeIn(150);
            				}, 300);

            				setTimeout(function() {
            					$('#N').fadeIn(150);
            				}, 375);

            				setTimeout(function() {
            					$('#G').fadeIn(150);
            				}, 450); */

        				/*$('#L').css('opacity', '.5');
        				$('#O').css('opacity', '.5');
        				$('#A').css('opacity', '.5');
        				$('#D').css('opacity', '.5');
        				$('#I').css('opacity', '.5');
        				$('#N').css('opacity', '.5');
        				$('#G').css('opacity', '.5');

        				setTimeout(function() {
        					$('#L').css('opacity', '1');
        					$('#O').css('opacity', '1');
        					$('#A').css('opacity', '1');
        					$('#D').css('opacity', '1');
        					$('#I').css('opacity', '1');
        					$('#N').css('opacity', '1');
        					$('#G').css('opacity', '1');
        				}, 1000);*/

        				$('#loading-container').css('opacity', '.4');

        				setTimeout(function(){
        					$('#loading-container').css('opacity', '1');
        				}, 1250);

        				/*$('#L').css('-webkit-transform', 'translateY(-5px)');
        				setTimeout(function() {
        					$('#L').css('-webkit-transform', 'translateY(0px)');
        				}, 300);

        				setTimeout(function() {
        					$('#O').css('-webkit-transform', 'translateY(-5px)');
        				}, 100);
        				setTimeout(function() {
        					$('#O').css('-webkit-transform', 'translateY(0px)');
        				}, 400);

        				setTimeout(function() {
        					$('#A').css('-webkit-transform', 'translateY(-5px)');
        				}, 200);
        				setTimeout(function() {
        					$('#A').css('-webkit-transform', 'translateY(0px)');
        				}, 500);

        				setTimeout(function() {
        					$('#D').css('-webkit-transform', 'translateY(-5px)');
        				}, 300);
        				setTimeout(function() {
        					$('#D').css('-webkit-transform', 'translateY(0px)');
        				}, 600);

        				setTimeout(function() {
        					$('#I').css('-webkit-transform', 'translateY(-5px)');
        				}, 400);
        				setTimeout(function() {
        					$('#I').css('-webkit-transform', 'translateY(0px)');
        				}, 700);

        				setTimeout(function() {
        					$('#N').css('-webkit-transform', 'translateY(-5px)');
        				}, 500);
        				setTimeout(function() {
        					$('#N').css('-webkit-transform', 'translateY(0px)');
        				}, 800);

        				setTimeout(function() {
        					$('#G').css('-webkit-transform', 'translateY(-5px)');
        				}, 600);
        				setTimeout(function() {
        					$('#G').css('-webkit-transform', 'translateY(0px)');
        				}, 900);*/

        				/*setTimeout(function() {
        					$('#L').fadeOut(150);
        				}, 1000);
        				setTimeout(function() {
        					$('#O').fadeOut(150);
        				}, 1075);
        				setTimeout(function() {
        					$('#A').fadeOut(150);
        				}, 1150);
        				setTimeout(function() {
        					$('#D').fadeOut(150);
        				}, 1225);
        				setTimeout(function() {
        					$('#I').fadeOut(150);
        				}, 1300);
        				setTimeout(function() {
        					$('#N').fadeOut(150);
        				}, 1375);
        				setTimeout(function() {
        					$('#G').fadeOut(150);
        				}, 1450);*/
        			}

        			/*if (int_loading_count == 0 || int_loading_count == 2 || int_loading_count == 4 || int_loading_count == 6 || int_loading_count == 8) {
        				$('#L').css('opacity', '0');
        				$('#O').css('opacity', '0');
        				$('#A').css('opacity', '0');
        				$('#D').css('opacity', '0');
        				$('#I').css('opacity', '0');
        				$('#N').css('opacity', '0');
        				$('#G').css('opacity', '0');
        			}
        			else {
        				$('#L').css('opacity', '1');
        				$('#O').css('opacity', '1');
        				$('#A').css('opacity', '1');
        				$('#D').css('opacity', '1');
        				$('#I').css('opacity', '1');
        				$('#N').css('opacity', '1');
        				$('#G').css('opacity', '1');
        			}*/

        			// $('#L').addClass('zoomOut');
        			/*$('#L').css('opacity', '.7');

        			setTimeout(function() {
        				// $('#O').addClass('zoomOut');
        				$('#L').css('opacity', '1');
        				$('#O').css('opacity', '.7');
        			}, 75);

        			setTimeout(function() {
        				$('#O').css('opacity', '1');
        				$('#A').css('opacity', '.7');
        			}, 150);

        			setTimeout(function() {
        				$('#A').css('opacity', '1');
        				$('#D').css('opacity', '.7');
        			}, 225);

        			setTimeout(function() {
        				$('#D').css('opacity', '1');
        				$('#I').css('opacity', '.7');
        			}, 300);

        			setTimeout(function() {
        				$('#I').css('opacity', '1');
        				$('#N').css('opacity', '.7');
        			}, 375);

        			setTimeout(function() {
        				$('#N').css('opacity', '1');
        				$('#G').css('opacity', '.7');
        			}, 450);*/

        			// $('#loading-circle').css('background-color', arr_colors[int_loading_count]);
        			int_loading_count += 1;
        			if (int_loading_count == 4) {
        				clearInterval(pulsing_circle_interval);
        				setTimeout(function() {
        					// $('#loading-container').addClass('animated slideOutUp');
        					$('#loading-container').fadeOut(300);
        				}, 500);
        				setTimeout(function() {
        					$('#content').fadeIn(0);
        					$('#content').addClass('animated bounceInUp');
        					// $('#content').addClass('animated slideInUp');
        				}, 750);
        			}

        			/*setTimeout(function() {
        				$('#loading-circle').css('opacity', '1');
        			}, 500);*/

        		}, 2500);

        	},

        	fn_reveal_content: function() {

        		/*setTimeout(function() {
        			$('#loading-circle').addClass('animated zoomOut');
        			$('#content').addClass('animated slideInUp');
        		}, 300);*/

        	},

        	fn_hide_rendering_icons: function() {

        		setTimeout(function() {
        			$('.rendering-container').addClass('display-none');
        		}, 3000);

        	},

        	fn_change_category: function(e) {

        		if (this.current_category == 'world') {
        			$('#category-container').removeClass('world')
        			$('#category-container').addClass('us');
        			$('#category-text').html('us');
        			this.current_category = 'us';
        		}

        		else if (this.current_category == 'us') {
        			$('#category-container').removeClass('us')
        			$('#category-container').addClass('sports');
        			$('#category-text').html('sports');
        			this.current_category = 'sports';
        		}

        		else if (this.current_category == 'sports') {
        			$('#category-container').removeClass('sports')
        			$('#category-container').addClass('business');
        			$('#category-text').html('business');
        			this.current_category = 'business';
        		}

        		else if (this.current_category == 'business') {
        			$('#category-container').removeClass('business')
        			$('#category-container').addClass('technology');
        			$('#category-text').html('technology');
        			this.current_category = 'technology';
        		}

        		else if (this.current_category == 'technology') {
        			$('#category-container').removeClass('technology')
        			$('#category-container').addClass('entertainment');
        			$('#category-text').html('entertainment');
        			this.current_category = 'entertainment';
        		}

        		else {
        			$('#category-container').removeClass('entertainment')
        			$('#category-container').addClass('world');
        			$('#category-text').html('world');
        			this.current_category = 'world';
        		}

        		if ($('.upvote-container').hasClass('upvoted') == true) {
        			$('.upvote-container').removeClass('us world sports business technology entertainment');
        			$('.upvote-text').removeClass('us world sports business technology entertainment');
        			$('.upvote-icon').removeClass('us world sports business technology entertainment');
        			$('.upvote-container').addClass(this.current_category);
        			$('.upvote-text').addClass(this.current_category);
        			$('.upvote-icon').addClass(this.current_category);
        		}

        	},

		fn_tap_upvote: function() {

			if ($('.upvote-container').hasClass('upvoted') == true) {

				$('.upvote-container').removeClass('upvoted');

				setTimeout(function() {
        				$('.upvote-container').removeClass('tapped us world sports business technology entertainment');
        				$('.upvote-text').removeClass('us world sports business technology entertainment');
        				$('.upvote-text').html(30);
        				$('.upvote-icon').removeClass('us world sports business technology entertainment');
        			}, 300);
        		}

			else {

				$('.upvote-container').addClass('upvoted');
    				setTimeout(function() {
        				$('.upvote-container').addClass('tapped ' + that.current_category);
        				$('.upvote-text').addClass(that.current_category);
        				$('.upvote-text').html(31);
        				$('.upvote-icon').addClass(that.current_category);
    				}, 300);
			}

		},

	};

	return Upvote;

})(window, document);
<commit_msg>Update upvote.js<commit_after>var Upvote = (function (window, document) {

	that = this;

	// endEvent = hasTouch ? 'touchend' : 'mouseup',

	Upvote = function (opts) {

		that = this;

		this.current_category = 'us';

		// ---------------------------------------------------------
		// Muench launch
		// ---------------------------------------------------------

		// Options from user
		for (i in opts) this.options[i] = opts[i];

		// ---------------------------------------------------------
		// Skimmin launch
		// ---------------------------------------------------------

        	$('.upvote-container').on('click', this.fn_tap_upvote.bind(this));
        	$('.category-container').on('click', this.fn_change_category.bind(this));

		window.addEventListener('load', function() {
		    FastClick.attach(document.body);
		}, false);

		this.fn_hide_rendering_icons();
		this.fn_loading();
       		this.fn_reveal_content();

    	};

	Upvote.prototype = {

		// ------------------------------------------------------
		// Skimmin Core Functions
		// ------------------------------------------------------

        	// primarily used to hide slow rendering
        	fn_loading: function() {

            	that = this;

            	$('#L').fadeIn(0);
            	$('#L').addClass('animated zoomIn');

            	setTimeout(function() {
            		$('#O').fadeIn(0);
            		$('#O').addClass('animated zoomIn');
            	}, 65);

            	setTimeout(function() {
            		$('#A').fadeIn(0);
            		$('#A').addClass('animated zoomIn');
            	}, 130);

            	setTimeout(function() {
            		$('#D').fadeIn(0);
            		$('#D').addClass('animated zoomIn');
            	}, 195);

            	setTimeout(function() {
            		$('#I').fadeIn(0);
            		$('#I').addClass('animated zoomIn');
            	}, 260);

            	setTimeout(function() {
            		$('#N').fadeIn(0);
            		$('#N').addClass('animated zoomIn');
            	}, 325);

            	setTimeout(function() {
            		$('#G').fadeIn(0);
            		$('#G').addClass('animated zoomIn');
            	}, 390);

            	/*setTimeout(function() {
            		$('#loading-container').addClass('animated pulse');
            	}, 1500);*/

            	/*setTimeout(function() {
            		$('#L').removeClass('animated zoomIn');
            		$('#O').removeClass('animated zoomIn');
            		$('#A').removeClass('animated zoomIn');
            		$('#D').removeClass('animated zoomIn');
            		$('#I').removeClass('animated zoomIn');
            		$('#N').removeClass('animated zoomIn');
            		$('#G').removeClass('animated zoomIn');
            	}, 1500);*/

            	int_loading_count = 0;
            	arr_colors = Array('rgba(52, 152, 219, 1)', 'rgba(255, 140, 0, 1)', 'rgba(231, 76, 60, 1)', 'rgba(0, 128, 0, .9)', 'rgba(0, 154, 136, .9)', 'rgba(164, 121, 228, 1)');

            	// bool_kill_next_loop = false;

        		// $('#loading-circle').addClass('animated pulse');

        	pulsing_circle_interval = setInterval(function() {

        			/*$('#L').fadeOut(300);
        			$('#O').fadeOut(300);
        			$('#A').fadeOut(300);
        			$('#D').fadeOut(300);
        			$('#I').fadeOut(300);
        			$('#N').fadeOut(300);
        			$('#G').fadeOut(300);*/

        			if (int_loading_count == 0 || int_loading_count == 1 || int_loading_count == 2 || int_loading_count == 3) {

        				/*$('#L').fadeIn(150);

            				setTimeout(function() {
            					$('#O').fadeIn(150);
            				}, 75);

            				setTimeout(function() {
            					$('#A').fadeIn(150);
            				}, 150);

            				setTimeout(function() {
            					$('#D').fadeIn(150);
            				}, 225);

            				setTimeout(function() {
            					$('#I').fadeIn(150);
            				}, 300);

            				setTimeout(function() {
            					$('#N').fadeIn(150);
            				}, 375);

            				setTimeout(function() {
            					$('#G').fadeIn(150);
            				}, 450); */

        				/*$('#L').css('opacity', '.5');
        				$('#O').css('opacity', '.5');
        				$('#A').css('opacity', '.5');
        				$('#D').css('opacity', '.5');
        				$('#I').css('opacity', '.5');
        				$('#N').css('opacity', '.5');
        				$('#G').css('opacity', '.5');

        				setTimeout(function() {
        					$('#L').css('opacity', '1');
        					$('#O').css('opacity', '1');
        					$('#A').css('opacity', '1');
        					$('#D').css('opacity', '1');
        					$('#I').css('opacity', '1');
        					$('#N').css('opacity', '1');
        					$('#G').css('opacity', '1');
        				}, 1000);*/

        				$('#loading-container').css('opacity', '.3');

        				setTimeout(function(){
        					$('#loading-container').css('opacity', '1');
        				}, 1250);

        				/*$('#L').css('-webkit-transform', 'translateY(-5px)');
        				setTimeout(function() {
        					$('#L').css('-webkit-transform', 'translateY(0px)');
        				}, 300);

        				setTimeout(function() {
        					$('#O').css('-webkit-transform', 'translateY(-5px)');
        				}, 100);
        				setTimeout(function() {
        					$('#O').css('-webkit-transform', 'translateY(0px)');
        				}, 400);

        				setTimeout(function() {
        					$('#A').css('-webkit-transform', 'translateY(-5px)');
        				}, 200);
        				setTimeout(function() {
        					$('#A').css('-webkit-transform', 'translateY(0px)');
        				}, 500);

        				setTimeout(function() {
        					$('#D').css('-webkit-transform', 'translateY(-5px)');
        				}, 300);
        				setTimeout(function() {
        					$('#D').css('-webkit-transform', 'translateY(0px)');
        				}, 600);

        				setTimeout(function() {
        					$('#I').css('-webkit-transform', 'translateY(-5px)');
        				}, 400);
        				setTimeout(function() {
        					$('#I').css('-webkit-transform', 'translateY(0px)');
        				}, 700);

        				setTimeout(function() {
        					$('#N').css('-webkit-transform', 'translateY(-5px)');
        				}, 500);
        				setTimeout(function() {
        					$('#N').css('-webkit-transform', 'translateY(0px)');
        				}, 800);

        				setTimeout(function() {
        					$('#G').css('-webkit-transform', 'translateY(-5px)');
        				}, 600);
        				setTimeout(function() {
        					$('#G').css('-webkit-transform', 'translateY(0px)');
        				}, 900);*/

        				/*setTimeout(function() {
        					$('#L').fadeOut(150);
        				}, 1000);
        				setTimeout(function() {
        					$('#O').fadeOut(150);
        				}, 1075);
        				setTimeout(function() {
        					$('#A').fadeOut(150);
        				}, 1150);
        				setTimeout(function() {
        					$('#D').fadeOut(150);
        				}, 1225);
        				setTimeout(function() {
        					$('#I').fadeOut(150);
        				}, 1300);
        				setTimeout(function() {
        					$('#N').fadeOut(150);
        				}, 1375);
        				setTimeout(function() {
        					$('#G').fadeOut(150);
        				}, 1450);*/
        			}

        			/*if (int_loading_count == 0 || int_loading_count == 2 || int_loading_count == 4 || int_loading_count == 6 || int_loading_count == 8) {
        				$('#L').css('opacity', '0');
        				$('#O').css('opacity', '0');
        				$('#A').css('opacity', '0');
        				$('#D').css('opacity', '0');
        				$('#I').css('opacity', '0');
        				$('#N').css('opacity', '0');
        				$('#G').css('opacity', '0');
        			}
        			else {
        				$('#L').css('opacity', '1');
        				$('#O').css('opacity', '1');
        				$('#A').css('opacity', '1');
        				$('#D').css('opacity', '1');
        				$('#I').css('opacity', '1');
        				$('#N').css('opacity', '1');
        				$('#G').css('opacity', '1');
        			}*/

        			// $('#L').addClass('zoomOut');
        			/*$('#L').css('opacity', '.7');

        			setTimeout(function() {
        				// $('#O').addClass('zoomOut');
        				$('#L').css('opacity', '1');
        				$('#O').css('opacity', '.7');
        			}, 75);

        			setTimeout(function() {
        				$('#O').css('opacity', '1');
        				$('#A').css('opacity', '.7');
        			}, 150);

        			setTimeout(function() {
        				$('#A').css('opacity', '1');
        				$('#D').css('opacity', '.7');
        			}, 225);

        			setTimeout(function() {
        				$('#D').css('opacity', '1');
        				$('#I').css('opacity', '.7');
        			}, 300);

        			setTimeout(function() {
        				$('#I').css('opacity', '1');
        				$('#N').css('opacity', '.7');
        			}, 375);

        			setTimeout(function() {
        				$('#N').css('opacity', '1');
        				$('#G').css('opacity', '.7');
        			}, 450);*/

        			// $('#loading-circle').css('background-color', arr_colors[int_loading_count]);
        			int_loading_count += 1;
        			if (int_loading_count == 4) {
        				clearInterval(pulsing_circle_interval);
        				setTimeout(function() {
        					// $('#loading-container').addClass('animated slideOutUp');
        					$('#loading-container').fadeOut(300);
        				}, 500);
        				setTimeout(function() {
        					$('#content').fadeIn(0);
        					$('#content').addClass('animated bounceInUp');
        					// $('#content').addClass('animated slideInUp');
        				}, 750);
        			}

        			/*setTimeout(function() {
        				$('#loading-circle').css('opacity', '1');
        			}, 500);*/

        		}, 2500);

        	},

        	fn_reveal_content: function() {

        		/*setTimeout(function() {
        			$('#loading-circle').addClass('animated zoomOut');
        			$('#content').addClass('animated slideInUp');
        		}, 300);*/

        	},

        	fn_hide_rendering_icons: function() {

        		setTimeout(function() {
        			$('.rendering-container').addClass('display-none');
        		}, 3000);

        	},

        	fn_change_category: function(e) {

        		if (this.current_category == 'world') {
        			$('#category-container').removeClass('world')
        			$('#category-container').addClass('us');
        			$('#category-text').html('us');
        			this.current_category = 'us';
        		}

        		else if (this.current_category == 'us') {
        			$('#category-container').removeClass('us')
        			$('#category-container').addClass('sports');
        			$('#category-text').html('sports');
        			this.current_category = 'sports';
        		}

        		else if (this.current_category == 'sports') {
        			$('#category-container').removeClass('sports')
        			$('#category-container').addClass('business');
        			$('#category-text').html('business');
        			this.current_category = 'business';
        		}

        		else if (this.current_category == 'business') {
        			$('#category-container').removeClass('business')
        			$('#category-container').addClass('technology');
        			$('#category-text').html('technology');
        			this.current_category = 'technology';
        		}

        		else if (this.current_category == 'technology') {
        			$('#category-container').removeClass('technology')
        			$('#category-container').addClass('entertainment');
        			$('#category-text').html('entertainment');
        			this.current_category = 'entertainment';
        		}

        		else {
        			$('#category-container').removeClass('entertainment')
        			$('#category-container').addClass('world');
        			$('#category-text').html('world');
        			this.current_category = 'world';
        		}

        		if ($('.upvote-container').hasClass('upvoted') == true) {
        			$('.upvote-container').removeClass('us world sports business technology entertainment');
        			$('.upvote-text').removeClass('us world sports business technology entertainment');
        			$('.upvote-icon').removeClass('us world sports business technology entertainment');
        			$('.upvote-container').addClass(this.current_category);
        			$('.upvote-text').addClass(this.current_category);
        			$('.upvote-icon').addClass(this.current_category);
        		}

        	},

		fn_tap_upvote: function() {

			if ($('.upvote-container').hasClass('upvoted') == true) {

				$('.upvote-container').removeClass('upvoted');

				setTimeout(function() {
        				$('.upvote-container').removeClass('tapped us world sports business technology entertainment');
        				$('.upvote-text').removeClass('us world sports business technology entertainment');
        				$('.upvote-text').html(30);
        				$('.upvote-icon').removeClass('us world sports business technology entertainment');
        			}, 300);
        		}

			else {

				$('.upvote-container').addClass('upvoted');
    				setTimeout(function() {
        				$('.upvote-container').addClass('tapped ' + that.current_category);
        				$('.upvote-text').addClass(that.current_category);
        				$('.upvote-text').html(31);
        				$('.upvote-icon').addClass(that.current_category);
    				}, 300);
			}

		},

	};

	return Upvote;

})(window, document);
<|endoftext|>


<commit_before>'use strict';

const Exchange = require ('./base/Exchange');
const { ExchangeError, BadRequest, DDoSProtection, RateLimitExceeded, BadSymbol } = require ('./base/errors');

module.exports = class wazirx extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'wazirx',
            'name': 'WazirX',
            'countries': ['IN'],
            'version': 'v2',
            'rateLimit': 100,
            'has': {
                'CORS': undefined,
                'fetchMarkets': true,
                'fetchCurrencies': false,
                'fetchTickers': true,
                'fetchTicker': true,
                'fetchOHLCV': false,
                'fetchOrderBook': true,
                'fetchTrades': true,
                'fetchTime': true,
                'fetchStatus': true,
                'privateAPI': true,
                'publicAPI': true,
            },
            'urls': {
                'logo': 'https://i0.wp.com/blog.wazirx.com/wp-content/uploads/2020/06/banner.png',
                'api': {
                    'spot': {
                        'v1': 'https://api.wazirx.com/sapi/v1',
                    },
                },
                'www': 'https://wazirx.com',
                'doc': 'https://docs.wazirx.com/#public-rest-api-for-wazirx',
            },
            'api': {
                'spot': {
                    'v1': {
                        'public': {
                            'get': {
                                'depth': 1,
                                'exchangeInfo': 1,
                                'ping': 1,
                                'systemStatus': 1,
                                'tickers/24hr': 1,
                                'ticker/24hr': 1,
                                'time': 1,
                                'trades': 1,
                            },
                        },
                        'private': {
                            'get': {
                                'account': 1,
                                'allOrders': 1,
                                'funds': 1,
                                'historicalTrades': 1,
                                'openOrders': 1,
                                'order': 1,
                            },
                            'post': {
                                'order': 1,
                                'order/test': 1,
                            },
                            'delete': {
                                'order': 1,
                                'openOrders': 1,
                            },
                        },
                    },
                },
            },
            'exceptions': {
                'exact': {
                    '1999': BadRequest, // {"code":1999,"message":"symbol is missing, symbol does not have a valid value"} message varies depending on the error
                    '2098': BadRequest, // {"code":2098,"message":"Request out of receiving window."}
                    '2136': RateLimitExceeded, // {"code":2136,"message":"Too many api request"}
                    '-1121': BadSymbol, // { "code": -1121, "message": "Invalid symbol." }
                    '2113': BadRequest, // {"code":2113,"message":"RecvWindow must be in range 1..60000"}
                    '2115': BadRequest, // {"code":2115,"message":"Signature not found."}
                },
            },
            'options': {
                'recvWindow': 10000,
            },
        });
    }

    async fetchMarkets (params = {}) {
        const response = await this.spotV1PublicGetExchangeInfo (params);
        //
        // {
        //     "timezone":"UTC",
        //     "serverTime":1641336850932,
        //     "symbols":[
        //     {
        //         "symbol":"btcinr",
        //         "status":"trading",
        //         "baseAsset":"btc",
        //         "quoteAsset":"inr",
        //         "baseAssetPrecision":5,
        //         "quoteAssetPrecision":0,
        //         "orderTypes":[
        //             "limit",
        //             "stop_limit"
        //         ],
        //         "isSpotTradingAllowed":true,
        //         "filters":[
        //             {
        //                 "filterType":"PRICE_FILTER",
        //                 "minPrice":"1",
        //                 "tickSize":"1"
        //             }
        //         ]
        //     },
        //
        const markets = this.safeValue (response, 'symbols', []);
        const result = [];
        for (let i = 0; i < markets.length; i++) {
            const entry = markets[i];
            const id = this.safeString (entry, 'symbol');
            const baseId = this.safeString (entry, 'baseAsset');
            const quoteId = this.safeString (entry, 'quoteAsset');
            const base = this.safeCurrencyCode (baseId);
            const quote = this.safeCurrencyCode (quoteId);
            const symbol = base + '/' + quote;
            const isSpot = this.safeValue (entry, 'isSpotTradingAllowed');
            const filters = this.safeValue (entry, 'filters');
            let minPrice = undefined;
            for (let j = 0; j < filters.length; j++) {
                const filter = filters[j];
                const filterType = this.safeString (filter, 'filterType');
                if (filterType === 'PRICE_FILTER') {
                    minPrice = this.safeNumber (filter, 'minPrice');
                }
            }
            const status = this.safeString (entry, 'status');
            const active = status === 'trading';
            const limits = {
                'price': {
                    'min': minPrice,
                    'max': undefined,
                },
                'amount': {
                    'min': undefined,
                    'max': undefined,
                },
                'cost': {
                    'min': undefined,
                    'max': undefined,
                },
            };
            const precision = {
                'price': this.safeInteger (entry, 'quoteAssetPrecision'),
                'amount': this.safeInteger (entry, 'baseAssetPrecision'),
            };
            result.push ({
                'info': entry,
                'symbol': symbol,
                'id': id,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'limits': limits,
                'precision': precision,
                'type': 'spot',
                'spot': isSpot,
                'active': active,
            });
        }
        return result;
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        if (limit !== undefined) {
            request['limit'] = limit; // [1, 5, 10, 20, 50, 100, 500, 1000]
        }
        const response = await this.spotV1PublicGetDepth (this.extend (request, params));
        //
        //     {
        //          "timestamp":1559561187,
        //          "asks":[
        //                     ["8540.0","1.5"],
        //                     ["8541.0","0.0042"]
        //                 ],
        //          "bids":[
        //                     ["8530.0","0.8814"],
        //                     ["8524.0","1.4"]
        //                 ]
        //      }
        //
        const timestamp = this.safeInteger (response, 'timestamp');
        return this.parseOrderBook (response, symbol, timestamp);
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        const ticker = await this.spotV1PublicGetTicker24hr (this.extend (request, params));
        //
        // {
        //     "symbol":"wrxinr",
        //     "baseAsset":"wrx",
        //     "quoteAsset":"inr",
        //     "openPrice":"94.77",
        //     "lowPrice":"92.7",
        //     "highPrice":"95.17",
        //     "lastPrice":"94.03",
        //     "volume":"1118700.0",
        //     "bidPrice":"94.02",
        //     "askPrice":"94.03",
        //     "at":1641382455000
        // }
        //
        return this.parseTicker (ticker, market);
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        const tickers = await this.spotV1PublicGetTickers24hr ();
        //
        // [
        //     {
        //        "symbol":"btcinr",
        //        "baseAsset":"btc",
        //        "quoteAsset":"inr",
        //        "openPrice":"3698486",
        //        "lowPrice":"3641155.0",
        //        "highPrice":"3767999.0",
        //        "lastPrice":"3713212.0",
        //        "volume":"254.11582",
        //        "bidPrice":"3715021.0",
        //        "askPrice":"3715022.0",
        //     }
        //     ...
        // ]
        //
        const result = {};
        for (let i = 0; i < tickers.length; i++) {
            const ticker = tickers[i];
            const marketId = this.safeString (ticker, 'symbol');
            const market = this.safeMarket (marketId, undefined);
            const symbol = market['symbol'];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        if (limit !== undefined) {
            request['limit'] = limit; // Default 500; max 1000.
        }
        const response = await this.spotV1PublicGetTrades (this.extend (request, params));
        // [
        //     {
        //         "id":322307791,
        //         "price":"93.7",
        //         "qty":"0.7",
        //         "quoteQty":"65.59",
        //         "time":1641386701000,
        //         "isBuyerMaker":false
        //     },
        // ]
        return this.parseTrades (response, market, since, limit);
    }

    parseTrade (trade, market = undefined) {
        //
        //     {
        //         "id":322307791,
        //         "price":"93.7",
        //         "qty":"0.7",
        //         "quoteQty":"65.59",
        //         "time":1641386701000,
        //         "isBuyerMaker":false
        //     }
        //
        const id = this.safeString (trade, 'id');
        const timestamp = this.parse8601 (this.safeString (trade, 'time'));
        const datetime = this.iso8601 (timestamp);
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        const isBuyerMaker = this.safeValue (trade, 'isBuyerMaker');
        const side = isBuyerMaker ? 'sell' : 'buy';
        const price = this.safeNumber (trade, 'price');
        const amount = this.safeNumber (trade, 'qty');
        const cost = this.safeNumber (trade, 'quoteQty');
        return this.safeTrade ({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': datetime,
            'symbol': symbol,
            'order': id,
            'type': undefined,
            'side': side,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': {
                'cost': undefined,
                'currency': undefined,
            },
        });
    }

    async fetchStatus (params = {}) {
        const response = await this.spotV1PublicGetSystemStatus (params);
        //
        //  { "status":"normal","message":"System is running normally." }
        //
        let status = this.safeString (response, 'status');
        status = (status === 'normal') ? 'ok' : 'maintenance';
        this.status = this.extend (this.status, {
            'status': status,
            'updated': this.milliseconds (),
        });
        return this.status;
    }

    async fetchTime (params = {}) {
        const response = await this.spotV1PublicGetTime (params);
        //
        //     {
        //         "serverTime":1635467280514
        //     }
        //
        return this.safeInteger (response, 'serverTime');
    }

    parseTicker (ticker, market = undefined) {
        //
        //     {
        //        "symbol":"btcinr",
        //        "baseAsset":"btc",
        //        "quoteAsset":"inr",
        //        "openPrice":"3698486",
        //        "lowPrice":"3641155.0",
        //        "highPrice":"3767999.0",
        //        "lastPrice":"3713212.0",
        //        "volume":"254.11582", // base volume
        //        "bidPrice":"3715021.0",
        //        "askPrice":"3715022.0",
        //        "at":1641382455000 // only on fetchTicker
        //     }
        //
        const marketId = this.safeString (ticker, 'symbol');
        market = this.safeMarket (marketId, market);
        const symbol = market['symbol'];
        const last = this.safeNumber (ticker, 'lastPrice');
        const open = this.safeNumber (ticker, 'openPrice');
        const high = this.safeNumber (ticker, 'highPrice');
        const low = this.safeNumber (ticker, 'lowPrice');
        const baseVolume = this.safeNumber (ticker, 'volume');
        const bid = this.safeNumber (ticker, 'bidPrice');
        const ask = this.safeNumber (ticker, 'askPrice');
        const timestamp = this.safeString (ticker, 'at');
        return this.safeTicker ({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': undefined,
            'ask': ask,
            'askVolume': undefined,
            'vwap': undefined,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': baseVolume,
            'quoteVolume': undefined,
            'info': ticker,
        }, market);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const accessibility = this.safeValue (api, 2);
        const marketType = this.safeValue (api, 0);
        const version = this.safeValue (api, 1);
        let url = this.urls['api'][marketType][version] + '/' + path;
        if (Object.keys (params).length) {
            url += '?' + this.urlencode (params);
        }
        if (accessibility === 'private') {
            this.checkRequiredCredentials ();
            let data = this.urlencode (this.extend ({ 'recvWindow': 10000, 'timestamp': this.milliseconds () }, params));
            const signature = this.hmac (data, this.secret, 'sha256');
            data += '&' + this.urlencode ({ 'signature': signature });
            url += '?' + data;
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'X-Api-Key': this.apiKey,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
        //
        // {"code":2098,"message":"Request out of receiving window."}
        //
        if ((code === 418) || (code === 429)) {
            throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body);
        }
        const errorCode = this.safeString (response, 'code');
        if (errorCode !== undefined) {
            const feedback = this.id + ' ' + body;
            this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
            throw new ExchangeError (feedback);
        }
    }
};
<commit_msg>add fetchBalance<commit_after>'use strict';

const Exchange = require ('./base/Exchange');
const { ExchangeError, BadRequest, DDoSProtection, RateLimitExceeded, BadSymbol } = require ('./base/errors');

module.exports = class wazirx extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'wazirx',
            'name': 'WazirX',
            'countries': ['IN'],
            'version': 'v2',
            'rateLimit': 100,
            'has': {
                'CORS': undefined,
                'fetchMarkets': true,
                'fetchCurrencies': false,
                'fetchTickers': true,
                'fetchTicker': true,
                'fetchOHLCV': false,
                'fetchOrderBook': true,
                'fetchTrades': true,
                'fetchTime': true,
                'fetchStatus': true,
                'privateAPI': true,
                'publicAPI': true,
            },
            'urls': {
                'logo': 'https://i0.wp.com/blog.wazirx.com/wp-content/uploads/2020/06/banner.png',
                'api': {
                    'spot': {
                        'v1': 'https://api.wazirx.com/sapi/v1',
                    },
                },
                'www': 'https://wazirx.com',
                'doc': 'https://docs.wazirx.com/#public-rest-api-for-wazirx',
            },
            'api': {
                'spot': {
                    'v1': {
                        'public': {
                            'get': {
                                'depth': 1,
                                'exchangeInfo': 1,
                                'ping': 1,
                                'systemStatus': 1,
                                'tickers/24hr': 1,
                                'ticker/24hr': 1,
                                'time': 1,
                                'trades': 1,
                            },
                        },
                        'private': {
                            'get': {
                                'account': 1,
                                'allOrders': 1,
                                'funds': 1,
                                'historicalTrades': 1,
                                'openOrders': 1,
                                'order': 1,
                            },
                            'post': {
                                'order': 1,
                                'order/test': 1,
                            },
                            'delete': {
                                'order': 1,
                                'openOrders': 1,
                            },
                        },
                    },
                },
            },
            'exceptions': {
                'exact': {
                    '1999': BadRequest, // {"code":1999,"message":"symbol is missing, symbol does not have a valid value"} message varies depending on the error
                    '2098': BadRequest, // {"code":2098,"message":"Request out of receiving window."}
                    '2136': RateLimitExceeded, // {"code":2136,"message":"Too many api request"}
                    '-1121': BadSymbol, // { "code": -1121, "message": "Invalid symbol." }
                    '2113': BadRequest, // {"code":2113,"message":"RecvWindow must be in range 1..60000"}
                    '2115': BadRequest, // {"code":2115,"message":"Signature not found."}
                },
            },
            'options': {
                'recvWindow': 10000,
            },
        });
    }

    async fetchMarkets (params = {}) {
        const response = await this.spotV1PublicGetExchangeInfo (params);
        //
        // {
        //     "timezone":"UTC",
        //     "serverTime":1641336850932,
        //     "symbols":[
        //     {
        //         "symbol":"btcinr",
        //         "status":"trading",
        //         "baseAsset":"btc",
        //         "quoteAsset":"inr",
        //         "baseAssetPrecision":5,
        //         "quoteAssetPrecision":0,
        //         "orderTypes":[
        //             "limit",
        //             "stop_limit"
        //         ],
        //         "isSpotTradingAllowed":true,
        //         "filters":[
        //             {
        //                 "filterType":"PRICE_FILTER",
        //                 "minPrice":"1",
        //                 "tickSize":"1"
        //             }
        //         ]
        //     },
        //
        const markets = this.safeValue (response, 'symbols', []);
        const result = [];
        for (let i = 0; i < markets.length; i++) {
            const entry = markets[i];
            const id = this.safeString (entry, 'symbol');
            const baseId = this.safeString (entry, 'baseAsset');
            const quoteId = this.safeString (entry, 'quoteAsset');
            const base = this.safeCurrencyCode (baseId);
            const quote = this.safeCurrencyCode (quoteId);
            const symbol = base + '/' + quote;
            const isSpot = this.safeValue (entry, 'isSpotTradingAllowed');
            const filters = this.safeValue (entry, 'filters');
            let minPrice = undefined;
            for (let j = 0; j < filters.length; j++) {
                const filter = filters[j];
                const filterType = this.safeString (filter, 'filterType');
                if (filterType === 'PRICE_FILTER') {
                    minPrice = this.safeNumber (filter, 'minPrice');
                }
            }
            const status = this.safeString (entry, 'status');
            const active = status === 'trading';
            const limits = {
                'price': {
                    'min': minPrice,
                    'max': undefined,
                },
                'amount': {
                    'min': undefined,
                    'max': undefined,
                },
                'cost': {
                    'min': undefined,
                    'max': undefined,
                },
            };
            const precision = {
                'price': this.safeInteger (entry, 'quoteAssetPrecision'),
                'amount': this.safeInteger (entry, 'baseAssetPrecision'),
            };
            result.push ({
                'info': entry,
                'symbol': symbol,
                'id': id,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'limits': limits,
                'precision': precision,
                'type': 'spot',
                'spot': isSpot,
                'active': active,
            });
        }
        return result;
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        if (limit !== undefined) {
            request['limit'] = limit; // [1, 5, 10, 20, 50, 100, 500, 1000]
        }
        const response = await this.spotV1PublicGetDepth (this.extend (request, params));
        //
        //     {
        //          "timestamp":1559561187,
        //          "asks":[
        //                     ["8540.0","1.5"],
        //                     ["8541.0","0.0042"]
        //                 ],
        //          "bids":[
        //                     ["8530.0","0.8814"],
        //                     ["8524.0","1.4"]
        //                 ]
        //      }
        //
        const timestamp = this.safeInteger (response, 'timestamp');
        return this.parseOrderBook (response, symbol, timestamp);
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        const ticker = await this.spotV1PublicGetTicker24hr (this.extend (request, params));
        //
        // {
        //     "symbol":"wrxinr",
        //     "baseAsset":"wrx",
        //     "quoteAsset":"inr",
        //     "openPrice":"94.77",
        //     "lowPrice":"92.7",
        //     "highPrice":"95.17",
        //     "lastPrice":"94.03",
        //     "volume":"1118700.0",
        //     "bidPrice":"94.02",
        //     "askPrice":"94.03",
        //     "at":1641382455000
        // }
        //
        return this.parseTicker (ticker, market);
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        const tickers = await this.spotV1PublicGetTickers24hr ();
        //
        // [
        //     {
        //        "symbol":"btcinr",
        //        "baseAsset":"btc",
        //        "quoteAsset":"inr",
        //        "openPrice":"3698486",
        //        "lowPrice":"3641155.0",
        //        "highPrice":"3767999.0",
        //        "lastPrice":"3713212.0",
        //        "volume":"254.11582",
        //        "bidPrice":"3715021.0",
        //        "askPrice":"3715022.0",
        //     }
        //     ...
        // ]
        //
        const result = {};
        for (let i = 0; i < tickers.length; i++) {
            const ticker = tickers[i];
            const marketId = this.safeString (ticker, 'symbol');
            const market = this.safeMarket (marketId, undefined);
            const symbol = market['symbol'];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        if (limit !== undefined) {
            request['limit'] = limit; // Default 500; max 1000.
        }
        const response = await this.spotV1PublicGetTrades (this.extend (request, params));
        // [
        //     {
        //         "id":322307791,
        //         "price":"93.7",
        //         "qty":"0.7",
        //         "quoteQty":"65.59",
        //         "time":1641386701000,
        //         "isBuyerMaker":false
        //     },
        // ]
        return this.parseTrades (response, market, since, limit);
    }

    parseTrade (trade, market = undefined) {
        //
        //     {
        //         "id":322307791,
        //         "price":"93.7",
        //         "qty":"0.7",
        //         "quoteQty":"65.59",
        //         "time":1641386701000,
        //         "isBuyerMaker":false
        //     }
        //
        const id = this.safeString (trade, 'id');
        const timestamp = this.parse8601 (this.safeString (trade, 'time'));
        const datetime = this.iso8601 (timestamp);
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        const isBuyerMaker = this.safeValue (trade, 'isBuyerMaker');
        const side = isBuyerMaker ? 'sell' : 'buy';
        const price = this.safeNumber (trade, 'price');
        const amount = this.safeNumber (trade, 'qty');
        const cost = this.safeNumber (trade, 'quoteQty');
        return this.safeTrade ({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': datetime,
            'symbol': symbol,
            'order': id,
            'type': undefined,
            'side': side,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': {
                'cost': undefined,
                'currency': undefined,
            },
        });
    }

    async fetchStatus (params = {}) {
        const response = await this.spotV1PublicGetSystemStatus (params);
        //
        //  { "status":"normal","message":"System is running normally." }
        //
        let status = this.safeString (response, 'status');
        status = (status === 'normal') ? 'ok' : 'maintenance';
        this.status = this.extend (this.status, {
            'status': status,
            'updated': this.milliseconds (),
        });
        return this.status;
    }

    async fetchTime (params = {}) {
        const response = await this.spotV1PublicGetTime (params);
        //
        //     {
        //         "serverTime":1635467280514
        //     }
        //
        return this.safeInteger (response, 'serverTime');
    }

    parseTicker (ticker, market = undefined) {
        //
        //     {
        //        "symbol":"btcinr",
        //        "baseAsset":"btc",
        //        "quoteAsset":"inr",
        //        "openPrice":"3698486",
        //        "lowPrice":"3641155.0",
        //        "highPrice":"3767999.0",
        //        "lastPrice":"3713212.0",
        //        "volume":"254.11582", // base volume
        //        "bidPrice":"3715021.0",
        //        "askPrice":"3715022.0",
        //        "at":1641382455000 // only on fetchTicker
        //     }
        //
        const marketId = this.safeString (ticker, 'symbol');
        market = this.safeMarket (marketId, market);
        const symbol = market['symbol'];
        const last = this.safeNumber (ticker, 'lastPrice');
        const open = this.safeNumber (ticker, 'openPrice');
        const high = this.safeNumber (ticker, 'highPrice');
        const low = this.safeNumber (ticker, 'lowPrice');
        const baseVolume = this.safeNumber (ticker, 'volume');
        const bid = this.safeNumber (ticker, 'bidPrice');
        const ask = this.safeNumber (ticker, 'askPrice');
        const timestamp = this.safeString (ticker, 'at');
        return this.safeTicker ({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': undefined,
            'ask': ask,
            'askVolume': undefined,
            'vwap': undefined,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': baseVolume,
            'quoteVolume': undefined,
            'info': ticker,
        }, market);
    }

    parseBalance (response) {
        const result = { };
        for (let i = 0; i < response.length; i++) {
            const balance = response[i];
            const id = this.safeString (balance, 'asset');
            const code = this.safeCurrencyCode (id);
            const account = this.account ();
            account['free'] = this.safeString (balance, 'free');
            account['used'] = this.safeString (balance, 'locked');
            result[code] = account;
        }
        return this.safeBalance (result);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const response = await this.spotV1PrivateGetFunds (params);
        //
        // [
        //       {
        //          "asset":"inr",
        //          "free":"0.0",
        //          "locked":"0.0"
        //       },
        // ]
        //
        return this.parseBalance (response);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const accessibility = this.safeValue (api, 2);
        const marketType = this.safeValue (api, 0);
        const version = this.safeValue (api, 1);
        let url = this.urls['api'][marketType][version] + '/' + path;
        if (Object.keys (params).length) {
            url += '?' + this.urlencode (params);
        }
        if (accessibility === 'private') {
            this.checkRequiredCredentials ();
            let data = this.urlencode (this.extend ({ 'recvWindow': this.options['recvWindow'], 'timestamp': this.milliseconds () }, params));
            const signature = this.hmac (data, this.secret, 'sha256');
            data += '&' + this.urlencode ({ 'signature': signature });
            url += '?' + data;
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'X-Api-Key': this.apiKey,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
        //
        // {"code":2098,"message":"Request out of receiving window."}
        //
        if ((code === 418) || (code === 429)) {
            throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body);
        }
        const errorCode = this.safeString (response, 'code');
        if (errorCode !== undefined) {
            const feedback = this.id + ' ' + body;
            this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
            throw new ExchangeError (feedback);
        }
    }
};
<|endoftext|>


<commit_before>// UI and App logic
import Media from './media';
import MyFile from './myFile';
import {setSkinFromUrl, setSkinFromFile} from './actionCreators';

import '../css/winamp.css';

module.exports = {
  media: Media.init(),
  init: function(options) {
    this.fileInput = document.createElement('input');
    this.fileInput.type = 'file';
    this.fileInput.style.display = 'none';

    this.dispatch({type: 'SET_VOLUME', volume: options.volume});
    this.dispatch({type: 'SET_BALANCE', balance: options.balance});
    this.loadFromUrl(options.mediaFile.url, options.mediaFile.name);
    this.dispatch(setSkinFromUrl(options.skinUrl));
    this._registerListeners();
    return this;
  },

  _registerListeners: function() {
    this.media.addEventListener('timeupdate', () => {
      this.dispatch({type: 'UPDATE_TIME_ELAPSED', elapsed: this.media.timeElapsed()});
    });

    this.media.addEventListener('ended', () => {
      this.dispatch({type: 'SET_MEDIA_STATUS', status: 'STOPPED'});
    });

    this.media.addEventListener('waiting', () => {
      this.dispatch({type: 'START_WORKING'});
    });

    this.media.addEventListener('stopWaiting', () => {
      this.dispatch({type: 'STOP_WORKING'});
    });

    this.fileInput.onchange = (e) => {
      this.loadFromFileReference(e.target.files[0]);
    };
  },

  /* Functions */
  seekToPercentComplete: function(percent) {
    this.media.seekToPercentComplete(percent);
  },

  seekForwardBy: function(seconds) {
    this.media.seekToTime(this.media.timeElapsed() + seconds);
  },

  openFileDialog: function() {
    this.fileInput.click();
  },

  loadFromFileReference: function(fileReference) {
    const file = new MyFile();
    file.setFileReference(fileReference);
    if (new RegExp('(wsz|zip)$', 'i').test(fileReference.name)) {
      this.dispatch(setSkinFromFile(file));
    } else {
      this.media.autoPlay = true;
      this.fileName = fileReference.name;
      file.processBuffer(this._loadBuffer.bind(this));
    }
  },

  // Used only for the initial load, since it must have a CORS header
  loadFromUrl: function(url, fileName) {
    if (!fileName) {
      this.fileName = url.split('/').pop();
    } else {
      this.fileName = fileName;
    }
    const file = new MyFile();
    file.setUrl(url);
    file.processBuffer(this._loadBuffer.bind(this));
  },

  /* Listeners */
  _loadBuffer: function(buffer) {
    function setMetaData() {
      const kbps = '128';
      const khz = Math.round(this.media.sampleRate() / 1000).toString();
      this.dispatch({type: 'SET_MEDIA_KBPS', kbps: kbps});
      this.dispatch({type: 'SET_MEDIA_KHZ', khz: khz});
      this.dispatch({type: 'SET_CHANNELS_COUNT', channels: this.media.channels()});
      this.dispatch({type: 'SET_MEDIA_NAME', name: this.fileName});
      this.dispatch({type: 'SET_MEDIA_LENGTH', length: this.media.duration()});
    }

    // Note, this will not happen right away
    this.media.loadBuffer(buffer, setMetaData.bind(this));
  }
};
<commit_msg>Add back playing media listener, refactor<commit_after>// UI and App logic
import Media from './media';
import MyFile from './myFile';
import {setSkinFromUrl, setSkinFromFile} from './actionCreators';

import '../css/winamp.css';

module.exports = {
  media: Media.init(),
  init: function(options) {
    this.fileInput = document.createElement('input');
    this.fileInput.type = 'file';
    this.fileInput.style.display = 'none';

    this.dispatch({type: 'SET_VOLUME', volume: options.volume});
    this.dispatch({type: 'SET_BALANCE', balance: options.balance});
    this.loadFromUrl(options.mediaFile.url, options.mediaFile.name);
    this.dispatch(setSkinFromUrl(options.skinUrl));

    this.media.addEventListener('timeupdate', () => {
      this.dispatch({type: 'UPDATE_TIME_ELAPSED', elapsed: this.media.timeElapsed()});
    });

    this.media.addEventListener('ended', () => {
      this.dispatch({type: 'SET_MEDIA_STATUS', status: 'STOPPED'});
    });

    this.media.addEventListener('playing', () => {
      this.dispatch({type: 'SET_MEDIA_STATUS', status: 'PLAYING'});
    });

    this.media.addEventListener('waiting', () => {
      this.dispatch({type: 'START_WORKING'});
    });

    this.media.addEventListener('stopWaiting', () => {
      this.dispatch({type: 'STOP_WORKING'});
    });

    this.fileInput.onchange = (e) => {
      this.loadFromFileReference(e.target.files[0]);
    };
    return this;
  },

  /* Functions */
  seekToPercentComplete: function(percent) {
    this.media.seekToPercentComplete(percent);
  },

  seekForwardBy: function(seconds) {
    this.media.seekToTime(this.media.timeElapsed() + seconds);
  },

  openFileDialog: function() {
    this.fileInput.click();
  },

  loadFromFileReference: function(fileReference) {
    const file = new MyFile();
    file.setFileReference(fileReference);
    if (new RegExp('(wsz|zip)$', 'i').test(fileReference.name)) {
      this.dispatch(setSkinFromFile(file));
    } else {
      this.media.autoPlay = true;
      this.fileName = fileReference.name;
      file.processBuffer(this._loadBuffer.bind(this));
    }
  },

  // Used only for the initial load, since it must have a CORS header
  loadFromUrl: function(url, fileName) {
    if (!fileName) {
      this.fileName = url.split('/').pop();
    } else {
      this.fileName = fileName;
    }
    const file = new MyFile();
    file.setUrl(url);
    file.processBuffer(this._loadBuffer.bind(this));
  },

  /* Listeners */
  _loadBuffer: function(buffer) {
    function setMetaData() {
      const kbps = '128';
      const khz = Math.round(this.media.sampleRate() / 1000).toString();
      this.dispatch({type: 'SET_MEDIA_KBPS', kbps: kbps});
      this.dispatch({type: 'SET_MEDIA_KHZ', khz: khz});
      this.dispatch({type: 'SET_CHANNELS_COUNT', channels: this.media.channels()});
      this.dispatch({type: 'SET_MEDIA_NAME', name: this.fileName});
      this.dispatch({type: 'SET_MEDIA_LENGTH', length: this.media.duration()});
    }

    // Note, this will not happen right away
    this.media.loadBuffer(buffer, setMetaData.bind(this));
  }
};
<|endoftext|>


<commit_before>import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";

import getStore from "./store";
import App from "./components/App";
import Hotkeys from "./hotkeys";
import Media from "./media";
import { setSkinFromUrl, loadMediaFromUrl } from "./actionCreators";

import { SET_AVALIABLE_SKINS } from "./actionTypes";

class Winamp {
  constructor(options) {
    this.options = options;

    this.media = new Media();
    this.store = getStore(this.media, this.options.__initialState);
  }

  _skinHasLoaded() {
    return new Promise(resolve => {
      const initialState = this.store.getState();
      if (!initialState.display.loading) {
        resolve();
        return;
      }
      const unsubscribe = this.store.subscribe(() => {
        const state = this.store.getState();
        if (!state.display.loading) {
          resolve();
          unsubscribe();
        }
      });
    });
  }

  async render(node) {
    this.store.dispatch(setSkinFromUrl(this.options.initialSkin.url));

    if (this.options.initialTrack && this.options.initialTrack.url) {
      this.store.dispatch(
        loadMediaFromUrl(
          this.options.initialTrack.url,
          this.options.initialTrack.name,
          "BUFFER"
        )
      );
    }
    if (this.options.avaliableSkins) {
      this.store.dispatch({
        type: SET_AVALIABLE_SKINS,
        skins: this.options.avaliableSkins
      });
    }

    new Hotkeys(this.store.dispatch);

    await this._skinHasLoaded();

    render(
      <Provider store={this.store}>
        <App media={this.media} />
      </Provider>,
      node
    );
  }
}

export default Winamp;
module.exports = Winamp;
<commit_msg>Abstract waiting for the store to get into a given state<commit_after>import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";

import getStore from "./store";
import App from "./components/App";
import Hotkeys from "./hotkeys";
import Media from "./media";
import { setSkinFromUrl, loadMediaFromUrl } from "./actionCreators";

import { SET_AVALIABLE_SKINS } from "./actionTypes";

// Return a promise that resolves when the store matches a predicate.
const storeHas = (store, predicate) =>
  new Promise(resolve => {
    if (predicate(store.getState())) {
      resolve();
      return;
    }
    const unsubscribe = store.subscribe(() => {
      if (predicate(store.getState())) {
        resolve();
        unsubscribe();
      }
    });
  });

class Winamp {
  constructor(options) {
    this.options = options;

    this.media = new Media();
    this.store = getStore(this.media, this.options.__initialState);
  }

  async render(node) {
    this.store.dispatch(setSkinFromUrl(this.options.initialSkin.url));

    if (this.options.initialTrack && this.options.initialTrack.url) {
      this.store.dispatch(
        loadMediaFromUrl(
          this.options.initialTrack.url,
          this.options.initialTrack.name,
          "BUFFER"
        )
      );
    }
    if (this.options.avaliableSkins) {
      this.store.dispatch({
        type: SET_AVALIABLE_SKINS,
        skins: this.options.avaliableSkins
      });
    }

    new Hotkeys(this.store.dispatch);

    // Wait for the skin to load.
    await storeHas(this.store, state => !state.display.loading);

    render(
      <Provider store={this.store}>
        <App media={this.media} />
      </Provider>,
      node
    );
  }
}

export default Winamp;
module.exports = Winamp;
<|endoftext|>


<commit_before>'use strict';

var CustomError    = require('es5-ext/lib/Error/custom')
  , isFunction     = require('es5-ext/lib/Function/is-function')
  , invoke         = require('es5-ext/lib/Function/invoke')
  , noop           = require('es5-ext/lib/Function/noop')
  , d              = require('es5-ext/lib/Object/descriptor')
  , extend         = require('es5-ext/lib/Object/extend')
  , callable       = require('es5-ext/lib/Object/valid-callable')
  , memoize        = require('memoizee/lib/regular')
  , ee             = require('event-emitter/lib/core')
  , isNode         = require('dom-ext/lib/Node/is-node')
  , validDocument  = require('dom-ext/lib/Document/valid-document')
  , makeElement    = require('dom-ext/lib/Document/prototype/make-element')
  , replaceContent = require('dom-ext/lib/Element/prototype/replace-content')
  , validSet       = require('set-collection/lib/valid-set')
  , stringify      = require('querystring').stringify
  , Db             = require('dbjs')

  , isArray = Array.isArray, map = Array.prototype.map
  , forEach = Array.prototype.forEach
  , Base = Db.Base
  , cellName = { td: true, th: true }
  , Table;

require('memoizee/lib/ext/method');

module.exports = Table = function (document, set/*, options*/) {
	var options = Object(arguments[2]), getList;
	this.document = validDocument(document);
	this.el = makeElement.bind(this.document);
	this.set = this.obj = validSet(set);
	this.sortMethods = {};
	this.current = {};

	this.render(options);

	// Columns
	if (options.columns != null) {

		// Sort methods
		forEach.call(options.columns, function (options, index) {
			var method, name;
			if (!options.sort) return;
			name = options.default ? '' : String(options.name || index);
			method = this.setSortMethod(name, options.sort, options.reverse);
			options.sortName = name;
		}, this);

		// Head
		if (this.head) {
			map.call(options.columns, function (options, index) {
				if (options.head) return this.headCellRender(options.head, options);
				return this.head.appendChild(document.createElement('th'));
			}, this).forEach(this.head.appendChild, this.head);
		}

		// Cells
		this.cellRenderers = map.call(options.columns, function (options, index) {
			var name;
			if ((set._type_ === 'namespace') && (typeof options === 'string')) {
				name = options;
				if (this.head) {
					this.head.appendChild(
						this.headCellRender(set.prototype.get(name)._label)
					);
				}
				return function (item) { return item.get(name); };
			}
			if (options.render != null) {
				return callable(options.render);
			} else if (options.name != null) {
				name = String(options.name);
				return function (item) { return item.get(name); };
			} else {
				return noop;
			}
		}, this);
	} else if (set._type_ === 'namespace') {
		this.cellRenderers = set.prototype.getPropertyNames(options.tag)
			.listByOrder().map(function (name) {
				if (this.head) {
					this.head.appendChild(
						this.headCellRenderer(this.get(name)._label.toDOM(this.document))
					);
				}
				return function (item) { return item.get(name); };
			});
	} else {
		throw new CustomError("Columns layout not provided", 'MISSING_COLUMNS');
	}

	// Rows
	if (!this.sortMethods['']) {
		if (options.sort) {
			this.setSortMethod('', options.sort, options.reverse);
		} else {
			if (set._type_ === 'namespace') getList = invoke('listByCreatedAt');
			else if (set.listByOrder) getList = invoke('listByOrder');
			else getList = invoke('list');
			this.sortMethods[''] = {
				getList: getList,
				reverse: false
			};
		}
	}

	this.reset();
};

ee(Object.defineProperties(Table.prototype, extend({
	reverse: d(false),
	render: d(function (options) {
		var el = makeElement.bind(this.document);
		this.dom = el('table', { class: 'dbjs' },
			options.head ? el('thead', this.head = el('tr')) : null,
			this.body = el('tbody'));
	}),
	headCellRender: d(function (dom, options) {
		var attr, sortName;
		if (isNode(dom) && (dom.nodeName.toLowerCase() === 'th')) return dom;
		if (options.sortName != null) {
			sortName = options.sortName;
			attr = this.document.createAttribute('href');
			this.on('change', function () {
				var data = {};
				if (sortName) data.sort = sortName;
				if ((this.current.sort === sortName) && !this.current.reverse) {
					data.reverse = true;
				}
				data = stringify(data);
				attr.value = data ? '?' + data : '.';
			});
			dom = this.el('a', attr, dom);
		}
		return this.el('th', dom);
	}),
	cellRender: d(function (render, item) {
		var dom = render(item);
		if (isNode(dom) && cellName.hasOwnProperty(dom.nodeName.toLowerCase())) {
			return dom;
		}
		return makeElement.call(this.document, 'td', dom);
	}),
	setSortMethod: d(function (name, data, reverse) {
		var getList;
		if (typeof data === 'string') {
			if (!this.obj.listByProperty) {
				throw new CustomError("Property sort not supported",
					'NO_SET_NAME_SORT_SUPPORT');
			}
			getList = function (set) { return set.listByProperty(data); };
		} else if (isFunction(data)) {
			if (data.length === 1) {
				getList = memoize(data);
			} else {
				getList = memoize(function (set) { return set.list(data); });
			}
		}
		return (this.sortMethods[name] = {
			getList: getList,
			reverse: Boolean(reverse)
		});
	}),
	reset: d(function (data) {
		data = Object(data);
		this.set = this.obj;
		this.sortBy(data.sort || '', data.reverse);
	}),
	sortBy: d(function (name, reverse) {
		var method;
		method = this.sortMethods[name];
		reverse = Boolean(reverse);
		this.current.sort = name;
		this.current.reverse = reverse;
		if (method.reverse) reverse = !reverse;
		this.sort(method.getList(this.set), reverse);
	}),
	sort: d(function (list, reverse) {
		if (!isArray(list)) {
			throw new CustomError("List must be an array", "ARRAY_EXPECTED");
		}
		if (list.obj !== this.set) {
			throw new CustomError("List doesn't match set", 'LIST_MISMATCH');
		}
		reverse = Boolean(reverse);
		if ((this.list === list) && (this.reverse === reverse)) return;
		this.emit('change');
		if (this.list !== list) {
			if (this.list) this.list.off('change', this.reload);
			list.on('change', this.reload);
		}
		this.list = list;
		this.reverse = reverse;
		this.reload();
	}),
	toDOM: d(function () { return this.dom; })
}, memoize(function (item) {
	return makeElement.call(this.document, 'tr',
		this.cellRenderers.map(function (render) {
			return this.cellRender(render, item);
		}, this));
}, { method: 'rowRender' }), d.binder({
	reload: d(function () {
		var list = this.list.map(this.rowRender);
		if (this.reverse) list.reverse();
		replaceContent.call(this.body, list);
	})
}))));

Object.defineProperty(Base, 'DOMTable', d(Table));

Object.defineProperty(Db, 'toDOMTable', d(function (document/*, options*/) {
	return new this.DOMTable(document, this, arguments[1]);
}));
<commit_msg>'class' option for table<commit_after>'use strict';

var CustomError    = require('es5-ext/lib/Error/custom')
  , isFunction     = require('es5-ext/lib/Function/is-function')
  , invoke         = require('es5-ext/lib/Function/invoke')
  , noop           = require('es5-ext/lib/Function/noop')
  , d              = require('es5-ext/lib/Object/descriptor')
  , extend         = require('es5-ext/lib/Object/extend')
  , callable       = require('es5-ext/lib/Object/valid-callable')
  , memoize        = require('memoizee/lib/regular')
  , ee             = require('event-emitter/lib/core')
  , isNode         = require('dom-ext/lib/Node/is-node')
  , validDocument  = require('dom-ext/lib/Document/valid-document')
  , makeElement    = require('dom-ext/lib/Document/prototype/make-element')
  , replaceContent = require('dom-ext/lib/Element/prototype/replace-content')
  , validSet       = require('set-collection/lib/valid-set')
  , stringify      = require('querystring').stringify
  , Db             = require('dbjs')

  , isArray = Array.isArray, map = Array.prototype.map
  , forEach = Array.prototype.forEach
  , Base = Db.Base
  , cellName = { td: true, th: true }
  , Table;

require('memoizee/lib/ext/method');

module.exports = Table = function (document, set/*, options*/) {
	var options = Object(arguments[2]), getList, classes;
	this.document = validDocument(document);
	this.el = makeElement.bind(this.document);
	this.set = this.obj = validSet(set);
	this.sortMethods = {};
	this.current = {};

	this.render(options);
	if (options.class != null) {
		classes = String(options.class);
		if (classes) {
			classes.split(' ').forEach(function (name) {
				this.add(name);
			}, this.dom.classList);
		}
	}

	// Columns
	if (options.columns != null) {

		// Sort methods
		forEach.call(options.columns, function (options, index) {
			var method, name;
			if (!options.sort) return;
			name = options.default ? '' : String(options.name || index);
			method = this.setSortMethod(name, options.sort, options.reverse);
			options.sortName = name;
		}, this);

		// Head
		if (this.head) {
			map.call(options.columns, function (options, index) {
				if (options.head) return this.headCellRender(options.head, options);
				return this.head.appendChild(document.createElement('th'));
			}, this).forEach(this.head.appendChild, this.head);
		}

		// Cells
		this.cellRenderers = map.call(options.columns, function (options, index) {
			var name;
			if ((set._type_ === 'namespace') && (typeof options === 'string')) {
				name = options;
				if (this.head) {
					this.head.appendChild(
						this.headCellRender(set.prototype.get(name)._label)
					);
				}
				return function (item) { return item.get(name); };
			}
			if (options.render != null) {
				return callable(options.render);
			} else if (options.name != null) {
				name = String(options.name);
				return function (item) { return item.get(name); };
			} else {
				return noop;
			}
		}, this);
	} else if (set._type_ === 'namespace') {
		this.cellRenderers = set.prototype.getPropertyNames(options.tag)
			.listByOrder().map(function (name) {
				if (this.head) {
					this.head.appendChild(
						this.headCellRenderer(this.get(name)._label.toDOM(this.document))
					);
				}
				return function (item) { return item.get(name); };
			});
	} else {
		throw new CustomError("Columns layout not provided", 'MISSING_COLUMNS');
	}

	// Rows
	if (!this.sortMethods['']) {
		if (options.sort) {
			this.setSortMethod('', options.sort, options.reverse);
		} else {
			if (set._type_ === 'namespace') getList = invoke('listByCreatedAt');
			else if (set.listByOrder) getList = invoke('listByOrder');
			else getList = invoke('list');
			this.sortMethods[''] = {
				getList: getList,
				reverse: false
			};
		}
	}

	this.reset();
};

ee(Object.defineProperties(Table.prototype, extend({
	reverse: d(false),
	render: d(function (options) {
		var el = makeElement.bind(this.document);
		this.dom = el('table', { class: 'dbjs' },
			options.head ? el('thead', this.head = el('tr')) : null,
			this.body = el('tbody'));
	}),
	headCellRender: d(function (dom, options) {
		var attr, sortName;
		if (isNode(dom) && (dom.nodeName.toLowerCase() === 'th')) return dom;
		if (options.sortName != null) {
			sortName = options.sortName;
			attr = this.document.createAttribute('href');
			this.on('change', function () {
				var data = {};
				if (sortName) data.sort = sortName;
				if ((this.current.sort === sortName) && !this.current.reverse) {
					data.reverse = true;
				}
				data = stringify(data);
				attr.value = data ? '?' + data : '.';
			});
			dom = this.el('a', attr, dom);
		}
		return this.el('th', dom);
	}),
	cellRender: d(function (render, item) {
		var dom = render(item);
		if (isNode(dom) && cellName.hasOwnProperty(dom.nodeName.toLowerCase())) {
			return dom;
		}
		return makeElement.call(this.document, 'td', dom);
	}),
	setSortMethod: d(function (name, data, reverse) {
		var getList;
		if (typeof data === 'string') {
			if (!this.obj.listByProperty) {
				throw new CustomError("Property sort not supported",
					'NO_SET_NAME_SORT_SUPPORT');
			}
			getList = function (set) { return set.listByProperty(data); };
		} else if (isFunction(data)) {
			if (data.length === 1) {
				getList = memoize(data);
			} else {
				getList = memoize(function (set) { return set.list(data); });
			}
		}
		return (this.sortMethods[name] = {
			getList: getList,
			reverse: Boolean(reverse)
		});
	}),
	reset: d(function (data) {
		data = Object(data);
		this.set = this.obj;
		this.sortBy(data.sort || '', data.reverse);
	}),
	sortBy: d(function (name, reverse) {
		var method;
		method = this.sortMethods[name];
		reverse = Boolean(reverse);
		this.current.sort = name;
		this.current.reverse = reverse;
		if (method.reverse) reverse = !reverse;
		this.sort(method.getList(this.set), reverse);
	}),
	sort: d(function (list, reverse) {
		if (!isArray(list)) {
			throw new CustomError("List must be an array", "ARRAY_EXPECTED");
		}
		if (list.obj !== this.set) {
			throw new CustomError("List doesn't match set", 'LIST_MISMATCH');
		}
		reverse = Boolean(reverse);
		if ((this.list === list) && (this.reverse === reverse)) return;
		this.emit('change');
		if (this.list !== list) {
			if (this.list) this.list.off('change', this.reload);
			list.on('change', this.reload);
		}
		this.list = list;
		this.reverse = reverse;
		this.reload();
	}),
	toDOM: d(function () { return this.dom; })
}, memoize(function (item) {
	return makeElement.call(this.document, 'tr',
		this.cellRenderers.map(function (render) {
			return this.cellRender(render, item);
		}, this));
}, { method: 'rowRender' }), d.binder({
	reload: d(function () {
		var list = this.list.map(this.rowRender);
		if (this.reverse) list.reverse();
		replaceContent.call(this.body, list);
	})
}))));

Object.defineProperty(Base, 'DOMTable', d(Table));

Object.defineProperty(Db, 'toDOMTable', d(function (document/*, options*/) {
	return new this.DOMTable(document, this, arguments[1]);
}));
<|endoftext|>