<commit_before>import React from 'react-native';
import NavigationBar from 'react-native-navbar';
import { Icon } from 'react-native-icons';
import Dimensions from 'Dimensions';

import CameraRollView from './Camera-Roll';
import CameraLiveView from './Camera-Live';

import tips_api from '../../utils/tips.js';
import img_api from '../../utils/img.js';

import globals from '../../../globalVariables';

var window = Dimensions.get('window');

var {
  View,
  Text,
  Image,
  StyleSheet,
  TouchableHighlight,
  NavigatorIOS,
} = React;

class CameraDashboard extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      tip: null
    };
  }

  getRestaurant() {
    // sent from Map-Dashboard after arrival at a restaurant
    var props = this.props.route.props;

    if( !props || !props.restaurant ) {
      return null;
    }

    return props.restaurant;
  }

  goToCameraRollScreen() {
    var restaurant = this.getRestaurant();

    this.props.navigator.push({
      title: 'Camera Roll',
      component: CameraRollView,
      props: {
        restaurant
      },
      navigationBar: (
        <NavigationBar
          title="Camera Roll" />
      )
    });
  }

  goToCameraLiveScreen() {
    var restaurant = this.getRestaurant();

    this.props.navigator.push({
      title: 'Picture Time',
      props: {
        restaurant
      },
      component:  CameraLiveView,
    })
  }

  componentWillMount() {
    this.setState({
      tip: tips_api.getRandomTip(),
      img: img_api.getRandomImg()
    });
  }

  render () {
    return (
      <View style={styles.container}>
        <View style={styles.headlineContainer}>
          <Text style={styles.headline}>Upload a photo of your meal!</Text>
        </View>

        <View style={styles.imageTipContainer}>
          <Image style={styles.img} source={this.state.img} />
          <Text style={styles.tip}>{this.state.tip}</Text>
        </View>

        <View style={styles.btnContainer}>
          <TouchableHighlight
            style={styles.mainBtn}
            onPress={this.goToCameraLiveScreen.bind(this)}
            underlayColor={globals.primaryLight}>
            <View style={styles.innerBtn}>
              <Icon
                name='ion|ios-camera-outline'
                size={30}
                color='#ffffff'
                style={styles.icon}
              />
              <Text style={styles.btnText}>Let's take a photo</Text>
            </View>
          </TouchableHighlight>

          <TouchableHighlight
            onPress={this.goToCameraRollScreen.bind(this)}
            underlayColor='#ffffff'>
            <Text style={styles.secondaryBtn}>Choose from my camera roll</Text>
          </TouchableHighlight>
        </View>
      </View>
    );
  }
}

export default CameraDashboard;

let styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: '#FFFFFF',
  },
  imageTipContainer: {
    flex: 5,
    justifyContent: 'center',
    paddingLeft: 50,
    paddingRight: 50,
  },
  innerBtn: {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  headline: {
    fontSize: 36,
    textAlign: 'center',
    fontFamily: 'SanFranciscoDisplay-Light',
    color: globals.darkText,
  },
  headlineContainer: {
    flex: 2,
    paddingTop: 15,
    paddingLeft: 50,
    paddingRight: 50,
    justifyContent: 'center',
  },
  icon: {
    width: 30,
    height: 30,
    marginRight: 10,
  },
  img: {
    alignSelf: 'center',
    marginBottom: 15,
    width: 150,
    height: 150,
    borderRadius: 150/2,
  },
  tip: {
    color: globals.mediumText,
    textAlign: 'center',
    fontSize: 18,
    fontFamily: 'SanFranciscoText-Regular',
  },
  btnContainer: {
    flex: 3,
    justifyContent: 'center',
    alignItems: 'center',
  },
  mainBtn: {
    width: window.width - 30,
    marginBottom: 20,
    paddingTop: 17,
    paddingBottom: 17,
    borderRadius: 5,
    backgroundColor: globals.primaryDark,
  },
  btnText: {
    fontSize: 20,
    textAlign: 'center',
    color: '#ffffff',
    fontFamily: 'SanFranciscoText-Semibold',
  },
  secondaryBtn: {
    width: window.width - 30,
    fontSize: 18,
    textAlign: 'center',
    color: globals.primaryDark,
    fontFamily: 'SanFranciscoText-Regular',
  }
});
<commit_msg>[Save] add bg to camera dashboard<commit_after>import React from 'react-native';
import NavigationBar from 'react-native-navbar';
import { Icon } from 'react-native-icons';
import Dimensions from 'Dimensions';

import CameraRollView from './Camera-Roll';
import CameraLiveView from './Camera-Live';

import tips_api from '../../utils/tips.js';
import img_api from '../../utils/img.js';

import globals from '../../../globalVariables';

var window = Dimensions.get('window');

var {
  View,
  Text,
  Image,
  StyleSheet,
  TouchableHighlight,
  NavigatorIOS,
} = React;

class CameraDashboard extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      tip: null
    };
  }

  getRestaurant() {
    // sent from Map-Dashboard after arrival at a restaurant
    var props = this.props.route.props;

    if( !props || !props.restaurant ) {
      return null;
    }

    return props.restaurant;
  }

  goToCameraRollScreen() {
    var restaurant = this.getRestaurant();

    this.props.navigator.push({
      title: 'Camera Roll',
      component: CameraRollView,
      props: {
        restaurant
      },
      navigationBar: (
        <NavigationBar
          title="Camera Roll" />
      )
    });
  }

  goToCameraLiveScreen() {
    var restaurant = this.getRestaurant();

    this.props.navigator.push({
      title: 'Picture Time',
      props: {
        restaurant
      },
      component:  CameraLiveView,
    })
  }

  componentWillMount() {
    this.setState({
      tip: tips_api.getRandomTip(),
      img: img_api.getRandomImg()
    });
  }

  render () {
    return (
      <View style={styles.container}>
        <Image
          source={require('image!white-pattern-bg')}>
          <View style={styles.headlineContainer}>
            <Text style={styles.headline}>Upload a photo of your meal!</Text>
          </View>

          <View style={styles.imageTipContainer}>
            <Image style={styles.img} source={this.state.img} />
            <Text style={styles.tip}>{this.state.tip}</Text>
          </View>

          <View style={styles.btnContainer}>
            <TouchableHighlight
              style={styles.mainBtn}
              onPress={this.goToCameraLiveScreen.bind(this)}
              underlayColor={globals.primaryLight}>
              <View style={styles.innerBtn}>
                <Icon
                  name='ion|ios-camera-outline'
                  size={30}
                  color='#ffffff'
                  style={styles.icon}
                />
                <Text style={styles.btnText}>Let's take a photo</Text>
              </View>
            </TouchableHighlight>

            <TouchableHighlight
              onPress={this.goToCameraRollScreen.bind(this)}
              underlayColor='#ffffff'>
              <Text style={styles.secondaryBtn}>Choose from my camera roll</Text>
            </TouchableHighlight>
          </View>
        </Image>
      </View>
    );
  }
}

export default CameraDashboard;

let styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  imageTipContainer: {
    flex: 5,
    justifyContent: 'center',
    paddingLeft: 50,
    paddingRight: 50,
  },
  innerBtn: {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  headline: {
    fontSize: 36,
    textAlign: 'center',
    fontFamily: 'SanFranciscoDisplay-Light',
    color: globals.darkText,
  },
  headlineContainer: {
    flex: 2,
    paddingTop: 15,
    paddingLeft: 50,
    paddingRight: 50,
    justifyContent: 'center',
  },
  icon: {
    width: 30,
    height: 30,
    marginRight: 10,
  },
  img: {
    alignSelf: 'center',
    marginBottom: 15,
    width: 150,
    height: 150,
    borderRadius: 150/2,
  },
  tip: {
    color: globals.mediumText,
    textAlign: 'center',
    fontSize: 18,
    fontFamily: 'SanFranciscoText-Regular',
  },
  btnContainer: {
    flex: 3,
    justifyContent: 'center',
    alignItems: 'center',
  },
  mainBtn: {
    width: window.width - 30,
    marginBottom: 20,
    paddingTop: 17,
    paddingBottom: 17,
    borderRadius: 5,
    backgroundColor: globals.primaryDark,
  },
  btnText: {
    fontSize: 20,
    textAlign: 'center',
    color: '#ffffff',
    fontFamily: 'SanFranciscoText-Semibold',
  },
  secondaryBtn: {
    width: window.width - 30,
    fontSize: 18,
    textAlign: 'center',
    color: globals.primaryDark,
    fontFamily: 'SanFranciscoText-Regular',
  },
});
<|endoftext|>


<commit_before>(function() {

  mit.Backgrounds = {

    // Speeds and Velocities of Backgrounds

    cloud_bg_move_speed: 2,
    cloud_bg_vx: 0,

    backtree_bg_move_speed: 4,
    backtree_bg_vx: 0,

    fronttree_bg_move_speed: 6,
    fronttree_bg_vx: 0,

    ground_bg_move_speed: 10,
    ground_bg_vx: 0,

    grass_bg_move_speed: 10,
    grass_bg_vx: 0,

    log_x: 40,
    log_y: 0,

    init: function() {
      // Lets LOLZ
    },

    drawClouds: function(ctx) {
      // Clouds
      var clouds = new Image();
      clouds.src = 'img/clouds.png';

      clouds.width = mit.W;
      clouds.height = mit.H;

      ctx.drawImage(clouds, this.cloud_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(clouds, mit.W + this.cloud_bg_vx, 0, mit.W, mit.H);

      if (-this.cloud_bg_vx >= mit.W) {
        this.cloud_bg_vx = 0;
      }

      this.cloud_bg_vx -= this.cloud_bg_move_speed;
    },

    drawBackTrees: function(ctx) {
      // Back Trees
      var back_trees = new Image();
      back_trees.src = 'img/back_trees.png';

      back_trees.width = mit.W;
      back_trees.height = mit.H;

      ctx.drawImage(back_trees, this.backtree_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(back_trees, mit.W + this.backtree_bg_vx, 0, mit.W, mit.H);

      if (-this.backtree_bg_vx >= mit.W) {
        this.backtree_bg_vx = 0;
      }

      if (mit.game_started)
        this.backtree_bg_vx -= this.backtree_bg_move_speed;
    },

    drawFrontTrees: function(ctx) {
      // Front Trees
      var front_trees = new Image();
      front_trees.src = 'img/front_trees.png';

      front_trees.width = mit.W;
      front_trees.height = mit.H;

      ctx.drawImage(front_trees, this.fronttree_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(front_trees, mit.W + this.fronttree_bg_vx, 0, mit.W, mit.H);

      if (-this.fronttree_bg_vx >= mit.W) {
        this.fronttree_bg_vx = 0;
      }

      if (mit.game_started)
        this.fronttree_bg_vx -= this.fronttree_bg_move_speed;
    },

    drawGround: function(ctx) {
      // Ground
      var ground = new Image();
      ground.src = 'img/ground.png';

      ground.width = mit.W;
      ground.height = mit.H;

      ctx.drawImage(ground, this.ground_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(ground, mit.W + this.ground_bg_vx, 0, mit.W, mit.H);

      if (-this.ground_bg_vx >= mit.W) {
        this.ground_bg_vx = 0;
      }

      if (mit.game_started)
        this.ground_bg_vx -= this.ground_bg_move_speed;
    },

    drawGrass: function(ctx) {
      // Grass
      var grass = new Image();
      grass.src = 'img/grass.png';

      grass.width = mit.W;
      grass.height = mit.H;

      ctx.drawImage(grass, this.grass_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(grass, mit.W + this.grass_bg_vx, 0, mit.W, mit.H);

      if (-this.grass_bg_vx >= mit.W) {
        this.grass_bg_vx = 0;
      }

      if (mit.game_started)
        this.grass_bg_vx -= this.grass_bg_move_speed;
    },

    drawInitLog: function(ctx) {

      var log = new Image();
      log.src = 'img/log.png';

      this.log_y = mit.H-(log.height+45);

      ctx.drawImage(log, this.log_x, this.log_y);

      if (mit.game_started) {
        this.log_x -= this.grass_bg_move_speed;
      }
    },

    // Draw Awesome Backgrounds
    // Backgrounds have been made for 1000x500 dimensions
    draw: function(ctx) {

      // Draw Linear Gradient for real/pure BG (sky/water)
      var gradient = ctx.createLinearGradient(0, 0, 0, mit.H);
      gradient.addColorStop(0, '#06c4f4');
      gradient.addColorStop(1, '#7bd4f6');
      ctx.save();
      ctx.fillStyle = gradient;
      ctx.fillRect(0, 0, mit.W, mit.H);
      ctx.restore();

      // Clouds
      this.drawClouds(ctx);

      // Back Small Trees
      this.drawBackTrees(ctx);

      // Front Big Trees
      this.drawFrontTrees(ctx);

      // Drawing the initial wood log on which
      // Pappu gonna sit and bask in the cool and cozy
      // environment.
      if (this.log_x+100 > 0) {
        this.drawInitLog(ctx);
      }
      else if (!mit.game_started) {
        this.log_x = 40;
      }

      // Draw Ground now!
      this.drawGround(ctx);
    }

  };

  // Initializations
  mit.Backgrounds.init();

}());<commit_msg>Improving gameplay<commit_after>(function() {

  mit.Backgrounds = {

    // Speeds and Velocities of Backgrounds

    cloud_bg_move_speed: 2,
    cloud_bg_vx: 0,

    backtree_bg_move_speed: 3,
    backtree_bg_vx: 0,

    fronttree_bg_move_speed: 6,
    fronttree_bg_vx: 0,

    ground_bg_move_speed: 10,
    ground_bg_vx: 0,

    grass_bg_move_speed: 10,
    grass_bg_vx: 0,

    log_x: 40,
    log_y: 0,

    init: function() {
      // Lets LOLZ
    },

    drawClouds: function(ctx) {
      // Clouds
      var clouds = new Image();
      clouds.src = 'img/clouds.png';

      clouds.width = mit.W;
      clouds.height = mit.H;

      ctx.drawImage(clouds, this.cloud_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(clouds, mit.W + this.cloud_bg_vx, 0, mit.W, mit.H);

      if (-this.cloud_bg_vx >= mit.W) {
        this.cloud_bg_vx = 0;
      }

      this.cloud_bg_vx -= this.cloud_bg_move_speed;
    },

    drawBackTrees: function(ctx) {
      // Back Trees
      var back_trees = new Image();
      back_trees.src = 'img/back_trees.png';

      back_trees.width = mit.W;
      back_trees.height = mit.H;

      ctx.drawImage(back_trees, this.backtree_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(back_trees, mit.W + this.backtree_bg_vx, 0, mit.W, mit.H);

      if (-this.backtree_bg_vx >= mit.W) {
        this.backtree_bg_vx = 0;
      }

      if (mit.game_started)
        this.backtree_bg_vx -= this.backtree_bg_move_speed;
    },

    drawFrontTrees: function(ctx) {
      // Front Trees
      var front_trees = new Image();
      front_trees.src = 'img/front_trees.png';

      front_trees.width = mit.W;
      front_trees.height = mit.H;

      ctx.drawImage(front_trees, this.fronttree_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(front_trees, mit.W + this.fronttree_bg_vx, 0, mit.W, mit.H);

      if (-this.fronttree_bg_vx >= mit.W) {
        this.fronttree_bg_vx = 0;
      }

      if (mit.game_started)
        this.fronttree_bg_vx -= this.fronttree_bg_move_speed;
    },

    drawGround: function(ctx) {
      // Ground
      var ground = new Image();
      ground.src = 'img/ground.png';

      ground.width = mit.W;
      ground.height = mit.H;

      ctx.drawImage(ground, this.ground_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(ground, mit.W + this.ground_bg_vx, 0, mit.W, mit.H);

      if (-this.ground_bg_vx >= mit.W) {
        this.ground_bg_vx = 0;
      }

      if (mit.game_started)
        this.ground_bg_vx -= this.ground_bg_move_speed;
    },

    drawGrass: function(ctx) {
      // Grass
      var grass = new Image();
      grass.src = 'img/grass.png';

      grass.width = mit.W;
      grass.height = mit.H;

      ctx.drawImage(grass, this.grass_bg_vx, 0, mit.W, mit.H);
      ctx.drawImage(grass, mit.W + this.grass_bg_vx, 0, mit.W, mit.H);

      if (-this.grass_bg_vx >= mit.W) {
        this.grass_bg_vx = 0;
      }

      if (mit.game_started)
        this.grass_bg_vx -= this.grass_bg_move_speed;
    },

    drawInitLog: function(ctx) {

      var log = new Image();
      log.src = 'img/log.png';

      this.log_y = mit.H-(log.height+45);

      ctx.drawImage(log, this.log_x, this.log_y);

      if (mit.game_started) {
        this.log_x -= this.grass_bg_move_speed;
      }
    },

    // Draw Awesome Backgrounds
    // Backgrounds have been made for 1000x500 dimensions
    draw: function(ctx) {

      // Draw Linear Gradient for real/pure BG (sky/water)
      var gradient = ctx.createLinearGradient(0, 0, 0, mit.H);
      gradient.addColorStop(0, '#06c4f4');
      gradient.addColorStop(1, '#7bd4f6');
      ctx.save();
      ctx.fillStyle = gradient;
      ctx.fillRect(0, 0, mit.W, mit.H);
      ctx.restore();

      // Clouds
      this.drawClouds(ctx);

      // Back Small Trees
      this.drawBackTrees(ctx);

      // Front Big Trees
      this.drawFrontTrees(ctx);

      // Drawing the initial wood log on which
      // Pappu gonna sit and bask in the cool and cozy
      // environment.
      if (this.log_x+100 > 0) {
        this.drawInitLog(ctx);
      }
      else if (!mit.game_started) {
        this.log_x = 40;
      }

      // Draw Ground now!
      this.drawGround(ctx);
    }

  };

  // Initializations
  mit.Backgrounds.init();

}());<|endoftext|>


<commit_before>'use strict';

const ccxt = require ('ccxt')
    // , util = require ('util')
    , {
        isNode,
        isJsonEncodedObject,
        RequestTimeout,
        NetworkError,
        NotSupported,
        deepExtend,
        milliseconds,
    } = ccxt
    , { inflate, gunzip } = require ('./functions')
    , Future = require ('./Future')

module.exports = class Client {

    constructor (url, onMessageCallback, onErrorCallback, onCloseCallback, onConnectedCallback, config = {}) {
        const defaults = {
            url,
            onMessageCallback,
            onErrorCallback,
            onCloseCallback,
            onConnectedCallback,
            verbose: false, // verbose output
            protocols: undefined, // ws-specific protocols
            options: undefined, // ws-specific options
            futures: {},
            subscriptions: {},
            connected: undefined, // connection-related Future
            error: undefined, // stores low-level networking exception, if any
            connectionStarted: undefined, // initiation timestamp in milliseconds
            connectionEstablished: undefined, // success timestamp in milliseconds
            isConnected: false,
            connectionTimer: undefined, // connection-related setTimeout
            connectionTimeout: 10000, // in milliseconds, false to disable
            pingInterval: undefined, // stores the ping-related interval
            ping: undefined, // ping-function (if defined)
            keepAlive: 30000, // ping-pong keep-alive rate in milliseconds
            maxPingPongMisses: 2.0, // how many missing pongs to throw a RequestTimeout
            // timeout is not used atm
            // timeout: 30000, // throw if a request is not satisfied in 30 seconds, false to disable
            connection: undefined,
            startedConnecting: false,
            gunzip: false,
            inflate: false,
        }
        Object.assign (this, deepExtend (defaults, config))
        // connection-related Future
        this.connected = Future ()
    }

    future (messageHash) {
        if (Array.isArray (messageHash)) {
            const firstHash = messageHash[0]
            if (!this.futures[firstHash]) {
                const future = Future ()
                this.futures[firstHash] = future
                for (let i = 1; i < messageHash.length; i++) {
                    const hash = messageHash[i]
                    this.futures[hash] = future
                }
            }
            return this.futures[firstHash]
        } else {
            if (!this.futures[messageHash]) {
                this.futures[messageHash] = Future ()
            }
            return this.futures[messageHash]
        }
    }

    resolve (result, messageHash) {
        if (this.verbose && (messageHash === undefined)) {
            this.log (new Date (), 'resolve received undefined messageHash');
        }
        if (this.futures[messageHash]) {
            const promise = this.futures[messageHash]
            promise.resolve (result)
            delete this.futures[messageHash]
        }
        return result
    }

    reject (result, messageHash = undefined) {
        if (messageHash) {
            if (this.futures[messageHash]) {
                const promise = this.futures[messageHash]
                promise.reject (result)
                delete this.futures[messageHash]
            }
        } else {
            const messageHashes = Object.keys (this.futures)
            for (let i = 0; i < messageHashes.length; i++) {
                this.reject (result, messageHashes[i])
            }
        }
        return result
    }

    log (... args) {
        console.log (... args)
    }

    connect (backoffDelay = 0) {
        throw new NotSupported ('connect() not implemented yet');
    }

    isOpen () {
        throw new NotSupported ('isOpen() not implemented yet');
    }

    reset (error) {
        this.clearConnectionTimeout ()
        this.clearPingInterval ()
        this.connected.reject (error)
        this.reject (error)
    }

    onConnectionTimeout () {
        if (!this.isOpen ()) {
            const error = new RequestTimeout ('Connection to ' + this.url + ' failed due to a connection timeout')
            this.onError (error)
            this.connection.close (1006)
        }
    }

    setConnectionTimeout () {
        if (this.connectionTimeout) {
            const onConnectionTimeout = this.onConnectionTimeout.bind (this)
            this.connectionTimer = setTimeout (onConnectionTimeout, this.connectionTimeout)
        }
    }

    clearConnectionTimeout () {
        if (this.connectionTimer) {
            this.connectionTimer = clearTimeout (this.connectionTimer)
        }
    }

    setPingInterval () {
        if (this.keepAlive) {
            const onPingInterval = this.onPingInterval.bind (this)
            this.pingInterval = setInterval (onPingInterval, this.keepAlive)
        }
    }

    clearPingInterval () {
        if (this.pingInterval) {
            this.pingInterval = clearInterval (this.pingInterval)
        }
    }

    onPingInterval () {
        if (this.keepAlive && this.isOpen ()) {
            const now = milliseconds ()
            this.lastPong = this.lastPong || now
            if ((this.lastPong + this.keepAlive * this.maxPingPongMisses) < now) {
                this.onError (new RequestTimeout ('Connection to ' + this.url + ' timed out due to a ping-pong keepalive missing on time'))
            } else {
                if (this.ping) {
                    this.send (this.ping (this))
                } else if (isNode) {
                    // can't do this inside browser
                    // https://stackoverflow.com/questions/10585355/sending-websocket-ping-pong-frame-from-browser
                    this.connection.ping ()
                } else {
                    // browsers handle ping-pong automatically therefore
                    // in a browser we update lastPong on every call to
                    // this function as if pong just came in to prevent the
                    // client from thinking it's a stalled connection
                    this.lastPong = now;
                }
            }
        }
    }

    onOpen () {
        if (this.verbose) {
            this.log (new Date (), 'onOpen')
        }
        this.connectionEstablished = milliseconds ()
        this.isConnected = true
        this.connected.resolve (this.url)
        // this.connection.terminate () // debugging
        this.clearConnectionTimeout ()
        this.setPingInterval ()
        this.onConnectedCallback (this)
    }

    // this method is not used at this time, because in JS the ws client will
    // respond to pings coming from the server with pongs automatically
    // however, some devs may want to track connection states in their app
    onPing () {
        if (this.verbose) {
            this.log (new Date (), 'onPing')
        }
    }

    onPong () {
        this.lastPong = milliseconds ()
        if (this.verbose) {
            this.log (new Date (), 'onPong')
        }
    }

    onError (error) {
        if (this.verbose) {
            this.log (new Date (), 'onError', error.message)
        }
        if (!(error instanceof ccxt.BaseError)) {
            // in case of ErrorEvent from node_modules/ws/lib/event-target.js
            error = new ccxt.NetworkError (error.message)
        }
        this.error = error
        this.reset (this.error)
        this.onErrorCallback (this, this.error)
    }

    onClose (event) {
        if (this.verbose) {
            this.log (new Date (), 'onClose', event)
        }
        if (!this.error) {
            // todo: exception types for server-side disconnects
            this.reset (new NetworkError ('connection closed by remote server, closing code ' + String (event.code)))
        }
        this.onCloseCallback (this, event)
    }

    // this method is not used at this time
    // but may be used to read protocol-level data like cookies, headers, etc
    onUpgrade (message) {
        if (this.verbose) {
            this.log (new Date (), 'onUpgrade')
        }
    }

    send (message) {
        if (this.verbose) {
            this.log (new Date (), 'sending', message)
        }
        message = (typeof message === 'string') ? message : JSON.stringify (message)
        this.connection.send (message)
    }

    close () {
        throw new NotSupported ('close() not implemented yet');
    }

    onMessage (message) {
        // if we use onmessage we get MessageEvent objects
        // MessageEvent {isTrusted: true, data: "{"e":"depthUpdate","E":1581358737706,"s":"ETHBTC",…"0.06200000"]],"a":[["0.02261300","0.00000000"]]}", origin: "wss://stream.binance.com:9443", lastEventId: "", source: null, …}
        message = message.data
        if (this.gunzip) {
            message = gunzip (message)
        } else if (this.inflate) {
            message = inflate (message)
        }
        try {
            if (message instanceof Buffer) {
                message = message.toString ()
            }
            if (isJsonEncodedObject (message)) {
                message = JSON.parse (message.replace (/:(\d{15,}),/g, ':"$1",'))
            }
            if (this.verbose) {
                this.log (new Date (), 'onMessage', message)
                // unlimited depth
                // this.log (new Date (), 'onMessage', util.inspect (message, false, null, true))
            }
        } catch (e) {
            this.log (new Date (), 'onMessage JSON.parse', e)
            // reset with a json encoding error ?
        }
        this.onMessageCallback (this, message)
    }
}
<commit_msg>make sure message is in bytes<commit_after>'use strict';

const ccxt = require ('ccxt')
    // , util = require ('util')
    , {
        isNode,
        isJsonEncodedObject,
        RequestTimeout,
        NetworkError,
        NotSupported,
        deepExtend,
        milliseconds,
    } = ccxt
    , { inflate, gunzip } = require ('./functions')
    , Future = require ('./Future')

module.exports = class Client {

    constructor (url, onMessageCallback, onErrorCallback, onCloseCallback, onConnectedCallback, config = {}) {
        const defaults = {
            url,
            onMessageCallback,
            onErrorCallback,
            onCloseCallback,
            onConnectedCallback,
            verbose: false, // verbose output
            protocols: undefined, // ws-specific protocols
            options: undefined, // ws-specific options
            futures: {},
            subscriptions: {},
            connected: undefined, // connection-related Future
            error: undefined, // stores low-level networking exception, if any
            connectionStarted: undefined, // initiation timestamp in milliseconds
            connectionEstablished: undefined, // success timestamp in milliseconds
            isConnected: false,
            connectionTimer: undefined, // connection-related setTimeout
            connectionTimeout: 10000, // in milliseconds, false to disable
            pingInterval: undefined, // stores the ping-related interval
            ping: undefined, // ping-function (if defined)
            keepAlive: 30000, // ping-pong keep-alive rate in milliseconds
            maxPingPongMisses: 2.0, // how many missing pongs to throw a RequestTimeout
            // timeout is not used atm
            // timeout: 30000, // throw if a request is not satisfied in 30 seconds, false to disable
            connection: undefined,
            startedConnecting: false,
            gunzip: false,
            inflate: false,
        }
        Object.assign (this, deepExtend (defaults, config))
        // connection-related Future
        this.connected = Future ()
    }

    future (messageHash) {
        if (Array.isArray (messageHash)) {
            const firstHash = messageHash[0]
            if (!this.futures[firstHash]) {
                const future = Future ()
                this.futures[firstHash] = future
                for (let i = 1; i < messageHash.length; i++) {
                    const hash = messageHash[i]
                    this.futures[hash] = future
                }
            }
            return this.futures[firstHash]
        } else {
            if (!this.futures[messageHash]) {
                this.futures[messageHash] = Future ()
            }
            return this.futures[messageHash]
        }
    }

    resolve (result, messageHash) {
        if (this.verbose && (messageHash === undefined)) {
            this.log (new Date (), 'resolve received undefined messageHash');
        }
        if (this.futures[messageHash]) {
            const promise = this.futures[messageHash]
            promise.resolve (result)
            delete this.futures[messageHash]
        }
        return result
    }

    reject (result, messageHash = undefined) {
        if (messageHash) {
            if (this.futures[messageHash]) {
                const promise = this.futures[messageHash]
                promise.reject (result)
                delete this.futures[messageHash]
            }
        } else {
            const messageHashes = Object.keys (this.futures)
            for (let i = 0; i < messageHashes.length; i++) {
                this.reject (result, messageHashes[i])
            }
        }
        return result
    }

    log (... args) {
        console.log (... args)
    }

    connect (backoffDelay = 0) {
        throw new NotSupported ('connect() not implemented yet');
    }

    isOpen () {
        throw new NotSupported ('isOpen() not implemented yet');
    }

    reset (error) {
        this.clearConnectionTimeout ()
        this.clearPingInterval ()
        this.connected.reject (error)
        this.reject (error)
    }

    onConnectionTimeout () {
        if (!this.isOpen ()) {
            const error = new RequestTimeout ('Connection to ' + this.url + ' failed due to a connection timeout')
            this.onError (error)
            this.connection.close (1006)
        }
    }

    setConnectionTimeout () {
        if (this.connectionTimeout) {
            const onConnectionTimeout = this.onConnectionTimeout.bind (this)
            this.connectionTimer = setTimeout (onConnectionTimeout, this.connectionTimeout)
        }
    }

    clearConnectionTimeout () {
        if (this.connectionTimer) {
            this.connectionTimer = clearTimeout (this.connectionTimer)
        }
    }

    setPingInterval () {
        if (this.keepAlive) {
            const onPingInterval = this.onPingInterval.bind (this)
            this.pingInterval = setInterval (onPingInterval, this.keepAlive)
        }
    }

    clearPingInterval () {
        if (this.pingInterval) {
            this.pingInterval = clearInterval (this.pingInterval)
        }
    }

    onPingInterval () {
        if (this.keepAlive && this.isOpen ()) {
            const now = milliseconds ()
            this.lastPong = this.lastPong || now
            if ((this.lastPong + this.keepAlive * this.maxPingPongMisses) < now) {
                this.onError (new RequestTimeout ('Connection to ' + this.url + ' timed out due to a ping-pong keepalive missing on time'))
            } else {
                if (this.ping) {
                    this.send (this.ping (this))
                } else if (isNode) {
                    // can't do this inside browser
                    // https://stackoverflow.com/questions/10585355/sending-websocket-ping-pong-frame-from-browser
                    this.connection.ping ()
                } else {
                    // browsers handle ping-pong automatically therefore
                    // in a browser we update lastPong on every call to
                    // this function as if pong just came in to prevent the
                    // client from thinking it's a stalled connection
                    this.lastPong = now;
                }
            }
        }
    }

    onOpen () {
        if (this.verbose) {
            this.log (new Date (), 'onOpen')
        }
        this.connectionEstablished = milliseconds ()
        this.isConnected = true
        this.connected.resolve (this.url)
        // this.connection.terminate () // debugging
        this.clearConnectionTimeout ()
        this.setPingInterval ()
        this.onConnectedCallback (this)
    }

    // this method is not used at this time, because in JS the ws client will
    // respond to pings coming from the server with pongs automatically
    // however, some devs may want to track connection states in their app
    onPing () {
        if (this.verbose) {
            this.log (new Date (), 'onPing')
        }
    }

    onPong () {
        this.lastPong = milliseconds ()
        if (this.verbose) {
            this.log (new Date (), 'onPong')
        }
    }

    onError (error) {
        if (this.verbose) {
            this.log (new Date (), 'onError', error.message)
        }
        if (!(error instanceof ccxt.BaseError)) {
            // in case of ErrorEvent from node_modules/ws/lib/event-target.js
            error = new ccxt.NetworkError (error.message)
        }
        this.error = error
        this.reset (this.error)
        this.onErrorCallback (this, this.error)
    }

    onClose (event) {
        if (this.verbose) {
            this.log (new Date (), 'onClose', event)
        }
        if (!this.error) {
            // todo: exception types for server-side disconnects
            this.reset (new NetworkError ('connection closed by remote server, closing code ' + String (event.code)))
        }
        this.onCloseCallback (this, event)
    }

    // this method is not used at this time
    // but may be used to read protocol-level data like cookies, headers, etc
    onUpgrade (message) {
        if (this.verbose) {
            this.log (new Date (), 'onUpgrade')
        }
    }

    send (message) {
        if (this.verbose) {
            this.log (new Date (), 'sending', message)
        }
        message = (typeof message === 'string') ? message : JSON.stringify (message)
        this.connection.send (message)
    }

    close () {
        throw new NotSupported ('close() not implemented yet');
    }

    onMessage (message) {
        // if we use onmessage we get MessageEvent objects
        // MessageEvent {isTrusted: true, data: "{"e":"depthUpdate","E":1581358737706,"s":"ETHBTC",…"0.06200000"]],"a":[["0.02261300","0.00000000"]]}", origin: "wss://stream.binance.com:9443", lastEventId: "", source: null, …}
        message = message.data
        if (message.byteLength !== undefined) {
            if (this.gunzip) {
                message = gunzip (message)
            } else if (this.inflate) {
                message = inflate (message)
            }
        }
        try {
            if (message instanceof Buffer) {
                message = message.toString ()
            }
            if (isJsonEncodedObject (message)) {
                message = JSON.parse (message.replace (/:(\d{15,}),/g, ':"$1",'))
            }
            if (this.verbose) {
                this.log (new Date (), 'onMessage', message)
                // unlimited depth
                // this.log (new Date (), 'onMessage', util.inspect (message, false, null, true))
            }
        } catch (e) {
            this.log (new Date (), 'onMessage JSON.parse', e)
            // reset with a json encoding error ?
        }
        this.onMessageCallback (this, message)
    }
}
<|endoftext|>


<commit_before>//Main game file
"use strict";
var game = game || {};
//Battle balls game file
game.battleBalls = {
    canvas : undefined,
    ctx : undefined,
    arena : undefined,
    playerIDs : [],
    players : {},
    sparks : [],
    booms : [],
    state : "START",

    init : function(){
        var me = this;
        // create new instance of socket.io
        var num = Math.floor(Math.random()*10);
        var name ='user'+num;
        //setting client's own properties (MIGHT NOT BE THE BEST PRACTICE);
        me.canvas = document.querySelector('#area');
        me.ctx = me.canvas.getContext('2d');
        me.ctx.lineWidth = 5;
        me.arena = game.createArena('white', me.canvas.height/2-10, me.canvas.width/2, me.canvas.height/2);
        //set up socket
        game.socketHandlers.init(me);
    },
    //main loop
    loop : function() {
        requestAnimationFrame(this.loop.bind(this));
        this.update();
        this.render();
    },
    //Sets start positions of all players
    setPlayerStarts : function() {
        var theta = 2*Math.PI/this.playerIDs.length; //Distance between each player
        var playerPositions = []; //Array of player start positions
        var startDist = 100; //Distance from center of map
        for(var i = 0; i < this.playerIDs.length; i++) {
            var point = {};
            point.x = this.canvas.width/2 + startDist*Math.cos(theta * i);
            point.y = this.canvas.height/2 + startDist*Math.sin(theta * i);
            playerPositions[i] = point;
        }
        var me = this;
        //set each player's position
        this.playerIDs.forEach(function(id, index){
            me.players[id].setPosition(playerPositions[index].x, playerPositions[index].y);
            //me.players[id].setPosition(me.canvas.width/2, me.canvas.height/2);
        });
    },
    //Update function for start menu
    updateStartMenu : function() {
        //console.log("START MENU");
        var canStart = true;
        var me = this;
        //Check if all players are ready
        me.playerIDs.forEach(function(id){
            if(!me.players[id].ready)
                canStart = false;
        });
        //If all players are read and there are more than 2
        if(me.playerIDs.length >= 2 && canStart) {
            //Begin game
            me.state = "GAME";
            game.socketHandlers.changeState("GAME");
            me.reset();
        }
    },
    //update function for end game screen
    updateGameEnd : function() {
        var canStart = true;
        var me = this;
        //Check if all players are ready
        this.playerIDs.forEach(function(id){
            if(!me.players[id].ready)
                canStart = false;
        });
        //If players are ready play again
        if(this.playerIDs.length >= 2 && canStart) {
            this.state = "GAME";
            game.socketHandlers.changeState("GAME");
            me.reset();
        }
    },
    //Main game loop
    updateGameLoop : function() {
        var dt = 0;
        var me = this;//save reference to this
        var KOes = 0;//number of players knocked out
        //Loop through all players
        me.playerIDs.forEach(function(id) {
            var player = me.players[id];//get player reference
            //If the player isn't knocked out
            if(!player.KOed) {
                //loop through the rest of the players for collisions
                me.playerIDs.forEach(function(id2){
                    var player2 = me.players[id2]; //reference to player to collide with
                    if(player2.id !== player.id){//if the players aren't the same
                        //Check collision if they aren't already colliding
                        if(game.physicsUtils.circleCollision(player, player2) && !player.colliding(player2)) {
                            //get impulse
                            var impulse = game.physicsUtils.getImpulse(player, player2, 1.5);
                            player.applyImpulse(impulse);//apply impluse to first player
                            player.collisions.push({player: player2, force: impulse});//Add collision to player obj
                            //get inverse of impulse
                            impulse.x = impulse.x * -1;
                            impulse.y = impulse.y * -1;
                            player2.applyImpulse(impulse);//send inverse to second player
                            player2.collisions.push({player: player, force: impulse});
                            //me.addSparks(player, player2, impulse);
                        }
                    }
                });
                //Bad code for resetting player if they leave the arena
                if(!me.arena.inBounds(player)) {
                    /*player.x = me.canvas.width/2;
          player.y = me.canvas.width/2;*/
                    player.KOed = true;
                }
                player.update(dt);//update the player
            } else {//if the player is koed
                KOes++;
            }
        });
        //If all players are knocked out end the game
        if(KOes >= me.playerIDs.length -1 && me.playerIDs.length > 1) {
            me.state = "END";
            game.socketHandlers.changeState("END");
        }
        //Loop through and update akk if the booms
        me.booms.forEach(function(boom, index, array){
            boom.update(dt);//update boom
            //make a copy of the boom
            var boomc = {
                x : boom.play.x,
                y : boom.play.y,
                velocity : {x:0, y:0},
                mass : boom.pow * 20,
                radius : boom.radius
            };
            //!!!!!!!!!!!!!!duplicate, make a helper function
            //Loop through all players
            me.playerIDs.forEach(function(id){
                var player = me.players[id];
                if(boom.id !== player.id){
                    //check for collison between a boom and other players
                    if(game.physicsUtils.circleCollision(player, boomc)) {
                        //get impulse
                        var impulse = {x:0, y:0};
                        impulse = game.physicsUtils.normalize(game.physicsUtils.vecDiff(player, boomc));
                        impulse.x *= 20;
                        impulse.y *= 20;
                        player.applyImpulse(impulse);
                        //me.addSparks(player, player2, impulse);
                    }
                }
            });
            //remove a boom if it is done
            if(boom.remove) {
                array.splice(index, 1);
            }
        });
        //loop through the sparks
        this.sparks.forEach(function(spark, index, array){
            spark.update(dt);
            if(spark.remove) {
                array.splice(index, 1);
            }
        });

        this.arena.radius -= 0.01;
    },
    //Update loop that handles all states
    update : function() {
        switch(this.state) {
            case "START" :
                this.updateStartMenu();
                break;
            case "GAME" :
                this.updateGameLoop();
                break;
            case "END" :
                this.updateGameEnd();
                break;
            default :
                break;
        }
    },
    //Resets game to state at start of game loop
    reset : function() {
      var me = this;
      me.arena.radius = me.canvas.height/2-10;
      this.playerIDs.forEach(function(id){
        me.players[id].ready = false;
        me.players[id].KOed = false;
      });
      me.setPlayerStarts();
    },
    /** Creates sparks based on an impulse
   * @param c1 : first circle object in collison
   * @param c2 : second circle object in collison
   * @param impulse : impulse the sparks are created from
   */
    addSparks : function(c1, c2, impulse) {
        /*var line = game.physicsUtils.getPerp(game.physicsUtils.getSlope(c1, c2));
    var x = c1.x - (c1.x - c2.x)/2;
    var y = c1.y - (c1.y - c2.y)/2;

    this.sparks.push(new game.Spark(x, y, line.x/5, line.y/5));
    this.sparks.push(new game.Spark(x, y, -line.x/5, -line.y/5));*/
    },
    //Draw function for the start menu
    renderStart : function() {
        var me = this;
        me.ctx.save();
        me.ctx.fillStyle = 'black';
        me.ctx.fillRect(0,0, me.canvas.width, me.canvas.height);
        me.ctx.restore();
        me.text(me.ctx, "Players in " + me.playerIDs.length, me.canvas.width/2, me.canvas.height/4, 100, "white");

        me.playerIDs.forEach(function(id, index) {
            var player = me.players[id];
            if(player.ready) {
                me.ctx.save();
                me.ctx.shadowBlur = 10;
                me.ctx.shadowColor = player.color;
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+index*50,50,player.color);
                me.ctx.restore();
            } else {
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+index*50,50,player.color);
            }
        });

    },
    /** Draws text to the screen
   * @param ctx : drawing context
   * @param string : text to be rendered
   * @param x : x coord of text
   * @param y : y coord of text
   * @param size : size of text
   * @param col : color of text
   */
    text: function(ctx, string, x, y, size, col) {
        ctx.save();
        ctx.font = size+'px BAAAAALLLLLLLLLLS';
        ctx.textAlign = "center";
        ctx.fillStyle = col;
        ctx.fillText(string, x, y);
        ctx.restore();
    },
    //Render in game screen
    renderGame : function() {
        var me = this;//save reference to this
        me.ctx.save();
        me.ctx.fillStyle = 'black';
        me.ctx.fillRect(0,0, me.canvas.width, me.canvas.height);
        me.ctx.restore();
        me.arena.render(me.ctx);//draw arena
        //loop through and draw each player
        me.playerIDs.forEach(function(id) {
            var player = me.players[id];
            player.render(me.ctx);
        });
        //loop through and draw each boom
        me.booms.forEach(function(boom) {
            boom.render(me.ctx);
        });
        //loop through and draw each spark
        me.sparks.forEach(function(spark) {
            spark.render(me.ctx);
        });
    },
    //Draw function for the end game screen
    renderEnd : function() {
        var me = this;
        me.ctx.save();
        me.ctx.fillStyle = 'black';
        me.ctx.fillRect(0,0, me.canvas.width, me.canvas.height);
        me.ctx.restore();
        me.text(me.ctx, "Game Over", me.canvas.width/2, me.canvas.height/4, 100, "white");
        me.text(me.ctx,"ready up to play again",me.canvas.width/2,me.canvas.height/4+50,50,"white");

        me.playerIDs.forEach(function(id, index) {
            var player = me.players[id];
            if(player.ready) {
                me.ctx.save();
                me.ctx.shadowBlur = 10;
                me.ctx.shadowColor = player.color;
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+25+index*50,50,player.color);
                me.ctx.restore();
            } else {
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+25+index*50,50,player.color);
            }
        });
    },
    //Main render function that handles different render states
    render : function() {
        switch(this.state) {
            case "START" :
                this.renderStart();
                break;
            case "GAME" :
                this.renderGame();
                break;
            case "END" :
                this.renderEnd();
                break;
            default :
                break;
        }
    }
}<commit_msg>Arena now has a delay before shrinking<commit_after>//Main game file
"use strict";
var game = game || {};
//Battle balls game file
game.battleBalls = {
    canvas : undefined,
    ctx : undefined,
    arena : undefined,
    playerIDs : [],
    players : {},
    sparks : [],
    booms : [],
    state : "START",
    arenaShrinkDelay : 1000,

    init : function(){
        var me = this;
        // create new instance of socket.io
        var num = Math.floor(Math.random()*10);
        var name ='user'+num;
        //setting client's own properties (MIGHT NOT BE THE BEST PRACTICE);
        me.canvas = document.querySelector('#area');
        me.ctx = me.canvas.getContext('2d');
        me.ctx.lineWidth = 5;
        me.arena = game.createArena('white', me.canvas.height/2-10, me.canvas.width/2, me.canvas.height/2);
        //set up socket
        game.socketHandlers.init(me);
    },
    //main loop
    loop : function() {
        requestAnimationFrame(this.loop.bind(this));
        this.update();
        this.render();
    },
    //Sets start positions of all players
    setPlayerStarts : function() {
        var theta = 2*Math.PI/this.playerIDs.length; //Distance between each player
        var playerPositions = []; //Array of player start positions
        var startDist = 100; //Distance from center of map
        for(var i = 0; i < this.playerIDs.length; i++) {
            var point = {};
            point.x = this.canvas.width/2 + startDist*Math.cos(theta * i);
            point.y = this.canvas.height/2 + startDist*Math.sin(theta * i);
            playerPositions[i] = point;
        }
        var me = this;
        //set each player's position
        this.playerIDs.forEach(function(id, index){
            me.players[id].setPosition(playerPositions[index].x, playerPositions[index].y);
            //me.players[id].setPosition(me.canvas.width/2, me.canvas.height/2);
        });
    },
    //Update function for start menu
    updateStartMenu : function() {
        //console.log("START MENU");
        var canStart = true;
        var me = this;
        //Check if all players are ready
        me.playerIDs.forEach(function(id){
            if(!me.players[id].ready)
                canStart = false;
        });
        //If all players are read and there are more than 2
        if(me.playerIDs.length >= 2 && canStart) {
            //Begin game
            me.state = "GAME";
            game.socketHandlers.changeState("GAME");
            me.reset();
        }
    },
    //update function for end game screen
    updateGameEnd : function() {
        var canStart = true;
        var me = this;
        //Check if all players are ready
        this.playerIDs.forEach(function(id){
            if(!me.players[id].ready)
                canStart = false;
        });
        //If players are ready play again
        if(this.playerIDs.length >= 2 && canStart) {
            this.state = "GAME";
            game.socketHandlers.changeState("GAME");
            me.reset();
        }
    },
    //Main game loop
    updateGameLoop : function() {
        var dt = 0;
        var me = this;//save reference to this
        var KOes = 0;//number of players knocked out
        //Loop through all players
        me.playerIDs.forEach(function(id) {
            var player = me.players[id];//get player reference
            //If the player isn't knocked out
            if(!player.KOed) {
                //loop through the rest of the players for collisions
                me.playerIDs.forEach(function(id2){
                    var player2 = me.players[id2]; //reference to player to collide with
                    if(player2.id !== player.id){//if the players aren't the same
                        //Check collision if they aren't already colliding
                        if(game.physicsUtils.circleCollision(player, player2) && !player.colliding(player2)) {
                            //get impulse
                            var impulse = game.physicsUtils.getImpulse(player, player2, 1.5);
                            player.applyImpulse(impulse);//apply impluse to first player
                            player.collisions.push({player: player2, force: impulse});//Add collision to player obj
                            //get inverse of impulse
                            impulse.x = impulse.x * -1;
                            impulse.y = impulse.y * -1;
                            player2.applyImpulse(impulse);//send inverse to second player
                            player2.collisions.push({player: player, force: impulse});
                            //me.addSparks(player, player2, impulse);
                        }
                    }
                });
                //Bad code for resetting player if they leave the arena
                if(!me.arena.inBounds(player)) {
                    /*player.x = me.canvas.width/2;
          player.y = me.canvas.width/2;*/
                    player.KOed = true;
                }
                player.update(dt);//update the player
            } else {//if the player is koed
                KOes++;
            }
        });
        //If all players are knocked out end the game
        if(KOes >= me.playerIDs.length -1 && me.playerIDs.length > 1) {
            me.state = "END";
            game.socketHandlers.changeState("END");
        }
        //Loop through and update akk if the booms
        me.booms.forEach(function(boom, index, array){
            boom.update(dt);//update boom
            //make a copy of the boom
            var boomc = {
                x : boom.play.x,
                y : boom.play.y,
                velocity : {x:0, y:0},
                mass : boom.pow * 20,
                radius : boom.radius
            };
            //!!!!!!!!!!!!!!duplicate, make a helper function
            //Loop through all players
            me.playerIDs.forEach(function(id){
                var player = me.players[id];
                if(boom.id !== player.id){
                    //check for collison between a boom and other players
                    if(game.physicsUtils.circleCollision(player, boomc)) {
                        //get impulse
                        var impulse = {x:0, y:0};
                        impulse = game.physicsUtils.normalize(game.physicsUtils.vecDiff(player, boomc));
                        impulse.x *= 20;
                        impulse.y *= 20;
                        player.applyImpulse(impulse);
                        //me.addSparks(player, player2, impulse);
                    }
                }
            });
            //remove a boom if it is done
            if(boom.remove) {
                array.splice(index, 1);
            }
        });
        //loop through the sparks
        this.sparks.forEach(function(spark, index, array){
            spark.update(dt);
            if(spark.remove) {
                array.splice(index, 1);
            }
        });
        if(this.arenaShrinkDelay < 1)
          this.arena.radius -= 0.03;
        else
          this.arenaShrinkDelay--;
    },
    //Update loop that handles all states
    update : function() {
        switch(this.state) {
            case "START" :
                this.updateStartMenu();
                break;
            case "GAME" :
                this.updateGameLoop();
                break;
            case "END" :
                this.updateGameEnd();
                break;
            default :
                break;
        }
    },
    //Resets game to state at start of game loop
    reset : function() {
      var me = this;
      me.arena.radius = me.canvas.height/2-10;
      this.playerIDs.forEach(function(id){
        me.players[id].ready = false;
        me.players[id].KOed = false;
      });
      me.setPlayerStarts();
    },
    /** Creates sparks based on an impulse
   * @param c1 : first circle object in collison
   * @param c2 : second circle object in collison
   * @param impulse : impulse the sparks are created from
   */
    addSparks : function(c1, c2, impulse) {
        /*var line = game.physicsUtils.getPerp(game.physicsUtils.getSlope(c1, c2));
    var x = c1.x - (c1.x - c2.x)/2;
    var y = c1.y - (c1.y - c2.y)/2;

    this.sparks.push(new game.Spark(x, y, line.x/5, line.y/5));
    this.sparks.push(new game.Spark(x, y, -line.x/5, -line.y/5));*/
    },
    //Draw function for the start menu
    renderStart : function() {
        var me = this;
        me.ctx.save();
        me.ctx.fillStyle = 'black';
        me.ctx.fillRect(0,0, me.canvas.width, me.canvas.height);
        me.ctx.restore();
        me.text(me.ctx, "Players in " + me.playerIDs.length, me.canvas.width/2, me.canvas.height/4, 100, "white");

        me.playerIDs.forEach(function(id, index) {
            var player = me.players[id];
            if(player.ready) {
                me.ctx.save();
                me.ctx.shadowBlur = 10;
                me.ctx.shadowColor = player.color;
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+index*50,50,player.color);
                me.ctx.restore();
            } else {
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+index*50,50,player.color);
            }
        });

    },
    /** Draws text to the screen
   * @param ctx : drawing context
   * @param string : text to be rendered
   * @param x : x coord of text
   * @param y : y coord of text
   * @param size : size of text
   * @param col : color of text
   */
    text: function(ctx, string, x, y, size, col) {
        ctx.save();
        ctx.font = size+'px BAAAAALLLLLLLLLLS';
        ctx.textAlign = "center";
        ctx.fillStyle = col;
        ctx.fillText(string, x, y);
        ctx.restore();
    },
    //Render in game screen
    renderGame : function() {
        var me = this;//save reference to this
        me.ctx.save();
        me.ctx.fillStyle = 'black';
        me.ctx.fillRect(0,0, me.canvas.width, me.canvas.height);
        me.ctx.restore();
        me.arena.render(me.ctx);//draw arena
        //loop through and draw each player
        me.playerIDs.forEach(function(id) {
            var player = me.players[id];
            player.render(me.ctx);
        });
        //loop through and draw each boom
        me.booms.forEach(function(boom) {
            boom.render(me.ctx);
        });
        //loop through and draw each spark
        me.sparks.forEach(function(spark) {
            spark.render(me.ctx);
        });
    },
    //Draw function for the end game screen
    renderEnd : function() {
        var me = this;
        me.ctx.save();
        me.ctx.fillStyle = 'black';
        me.ctx.fillRect(0,0, me.canvas.width, me.canvas.height);
        me.ctx.restore();
        me.text(me.ctx, "Game Over", me.canvas.width/2, me.canvas.height/4, 100, "white");
        me.text(me.ctx,"ready up to play again",me.canvas.width/2,me.canvas.height/4+50,50,"white");

        me.playerIDs.forEach(function(id, index) {
            var player = me.players[id];
            if(player.ready) {
                me.ctx.save();
                me.ctx.shadowBlur = 10;
                me.ctx.shadowColor = player.color;
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+25+index*50,50,player.color);
                me.ctx.restore();
            } else {
                me.text(me.ctx, "player "+(index+1),me.canvas.width/2,me.canvas.height/2+25+index*50,50,player.color);
            }
        });
    },
    //Main render function that handles different render states
    render : function() {
        switch(this.state) {
            case "START" :
                this.renderStart();
                break;
            case "GAME" :
                this.renderGame();
                break;
            case "END" :
                this.renderEnd();
                break;
            default :
                break;
        }
    }
}<|endoftext|>


<commit_before>import Component from '@ember/component';
import { inject as service } from '@ember/service';

const assetsPath = 'challenge/assets'

export default Component.extend({
  classNames: ["challenges-book-container zoom"],
  store: service(),
  router: service(),
  blocksGallery: service(),

  didInsertElement() {
    this.fileInputProyecto().addEventListener("change", (event) => {
      const archivo = event.target.files[0];

      if (archivo) {
        this.leerSolucionWeb(archivo)
      }

      this.limpiarInput(this.fileInputProyecto()); // Fuerza a que se pueda cargar dos o más veces el mismo archivo
      return false;
    });
  },

  leerSolucionWeb(archivo) {
    var reader = new FileReader();
    return new Promise((resolve, reject) => {
      reader.onerror = (err) => reject(err);
      reader.onload = (event) => this._loadChallenge(event.target.result, resolve);
      reader.readAsArrayBuffer(archivo);
    })
      .then((contenido) => this.cargarProyecto(contenido))
      .catch(alert)
  },

  _filenameToIdentifier(filename) { //Converts "desafio/assets/obstaculos/grass.png" to "obstaculos/grass"
    return filename.replace(`${assetsPath}/`, '').split('.')[0]
  },

  _isChallengeImage(filepath) {
    return filepath.startsWith(assetsPath) && filepath.toLowerCase().endsWith('.png')
  },

  async _imageContentToURL(content) {
    const blob = await content.blob('image/png')
    return URL.createObjectURL(blob)
  },

  async _getSceneImages(entries) {
    const imageEntries = Object.entries(entries).filter(entry => this._isChallengeImage(entry[0]))
    return await Promise.all(imageEntries.map(async ([filename, content]) => ({ id: this._filenameToIdentifier(filename), url: await this._imageContentToURL(content) })))
  },

  async _getChallengeJson(entries) {
    const arrayBuffer = await entries["challenge/challenge.json"].arrayBuffer();
    const jsonDesafioAsString = new TextDecoder().decode(arrayBuffer);
    return JSON.parse(jsonDesafioAsString);
  },

  //Zip and challenge.json definition: https://github.com/Program-AR/pilas-bloques/wiki/Expected-custom-challenge-zip-structure
  async _loadChallenge(theZipContent, resolve) {
    const { entries } = await unzipit.unzip(
      new Uint8Array(theZipContent)
    );
    //TODO: The custom challenge creator should verify that the custom challenge name does not conflict with any pre-existing challenge name.
    const challengeJson = await this._getChallengeJson(entries)
    const sceneImages = await this._getSceneImages(entries)
    const challengeCover = await this._imageContentToURL(entries[`${assetsPath}/splashChallenge.png`]);
    challengeJson.challengeCover = challengeCover
    challengeJson.imagesToPreload = sceneImages.map(image => image.url)
    //Currently it is not possible to define scenes in the json itself, like in the desafios.js file, but it can be made possible by replacing this line with "challengeJson.escena = challengeJson.sceneConstructor || `new CustomScene(...)"
    challengeJson.escena = `new CustomScene({grid:{spec:${JSON.stringify(challengeJson.grid)}}, images:${JSON.stringify(sceneImages)}})`
    this.blocksGallery.start()
    challengeJson.blocks.forEach(block => this._createBlock(block)) //Create all the custom blocks
    challengeJson.bloques = challengeJson.blocks.map(b => b.name) //The "bloques" attribute is the list with all the names of the blocks that the challenge uses
    //TODO: Chequear que los bloques instanciados en la blocksGallery tienen las propiedades que definimos en el json del zip.
    resolve(challengeJson)
  },

  _createBlock(aBlock) {
    const name = aBlock.name;
    const interactsWith = aBlock.interactsWith;
    const description = aBlock.description;
    const blockType = aBlock.type;

    if (blockType === "action") {
      const properties = {
        descripcion: description,
        icono: interactsWith + '.png',
        comportamiento: 'Recolectar',
        argumentos: `{etiqueta: '${interactsWith}'}`
      }
      this.blocksGallery.crearBloqueAccion(name, undefined, properties)
    }
    if (blockType === "sensor") {
      const object = aBlock.object;
      const properties = {
        descripcion: description,
        icono: object + '.png',
        funcionSensor: `tocando("${object}")`,
        esBool: true
      }
      this.blocksGallery.crearBloqueSensor(name, undefined, properties)
    }
  },

  cargarProyecto(desafio) {
    this.store.createRecord("desafio", desafio);
    this.router.transitionTo("desafio", desafio.id);
  },

  fileInputProyecto() {
    return document.getElementById("cargarProyectoInput");
  },

  limpiarInput(input) {
    input.value = null;
  },

  actions: {
    importarProyecto() {
      this.fileInputProyecto().click();
    },
  },
});
<commit_msg>removing TODO<commit_after>import Component from '@ember/component';
import { inject as service } from '@ember/service';

const assetsPath = 'challenge/assets'

export default Component.extend({
  classNames: ["challenges-book-container zoom"],
  store: service(),
  router: service(),
  blocksGallery: service(),

  didInsertElement() {
    this.fileInputProyecto().addEventListener("change", (event) => {
      const archivo = event.target.files[0];

      if (archivo) {
        this.leerSolucionWeb(archivo)
      }

      this.limpiarInput(this.fileInputProyecto()); // Fuerza a que se pueda cargar dos o más veces el mismo archivo
      return false;
    });
  },

  leerSolucionWeb(archivo) {
    var reader = new FileReader();
    return new Promise((resolve, reject) => {
      reader.onerror = (err) => reject(err);
      reader.onload = (event) => this._loadChallenge(event.target.result, resolve);
      reader.readAsArrayBuffer(archivo);
    })
      .then((contenido) => this.loadProyect(contenido))
      .catch(alert)
  },

  _filenameToIdentifier(filename) { //Converts "desafio/assets/obstaculos/grass.png" to "obstaculos/grass"
    return filename.replace(`${assetsPath}/`, '').split('.')[0]
  },

  _isChallengeImage(filepath) {
    return filepath.startsWith(assetsPath) && filepath.toLowerCase().endsWith('.png')
  },

  async _imageContentToURL(content) {
    const blob = await content.blob('image/png')
    return URL.createObjectURL(blob)
  },

  async _getSceneImages(entries) {
    const imageEntries = Object.entries(entries).filter(entry => this._isChallengeImage(entry[0]))
    return await Promise.all(imageEntries.map(async ([filename, content]) => ({ id: this._filenameToIdentifier(filename), url: await this._imageContentToURL(content) })))
  },

  async _getChallengeJson(entries) {
    const arrayBuffer = await entries["challenge/challenge.json"].arrayBuffer();
    const jsonDesafioAsString = new TextDecoder().decode(arrayBuffer);
    return JSON.parse(jsonDesafioAsString);
  },

  //Zip and challenge.json definition: https://github.com/Program-AR/pilas-bloques/wiki/Expected-custom-challenge-zip-structure
  async _loadChallenge(theZipContent, resolve) {
    const { entries } = await unzipit.unzip(
      new Uint8Array(theZipContent)
    );
    //TODO: The custom challenge creator should verify that the custom challenge name does not conflict with any pre-existing challenge name.
    const challengeJson = await this._getChallengeJson(entries)
    const sceneImages = await this._getSceneImages(entries)
    const challengeCover = await this._imageContentToURL(entries[`${assetsPath}/splashChallenge.png`]);
    challengeJson.challengeCover = challengeCover
    challengeJson.imagesToPreload = sceneImages.map(image => image.url)
    //Currently it is not possible to define scenes in the json itself, like in the desafios.js file, but it can be made possible by replacing this line with "challengeJson.escena = challengeJson.sceneConstructor || `new CustomScene(...)"
    challengeJson.escena = `new CustomScene({grid:{spec:${JSON.stringify(challengeJson.grid)}}, images:${JSON.stringify(sceneImages)}})`
    this.blocksGallery.start()
    challengeJson.blocks.forEach(block => this._createBlock(block)) //Create all the custom blocks
    challengeJson.bloques = challengeJson.blocks.map(b => b.name) //The "bloques" attribute is the list with all the names of the blocks that the challenge uses
    resolve(challengeJson)
  },

  _createBlock(aBlock) {
    const name = aBlock.name;
    const interactsWith = aBlock.interactsWith;
    const description = aBlock.description;
    const blockType = aBlock.type;

    if (blockType === "action") {
      const properties = {
        descripcion: description,
        icono: interactsWith + '.png',
        comportamiento: 'Recolectar',
        argumentos: `{etiqueta: '${interactsWith}'}`
      }
      this.blocksGallery.crearBloqueAccion(name, undefined, properties)
    }
    if (blockType === "sensor") {
      const object = aBlock.object;
      const properties = {
        descripcion: description,
        icono: object + '.png',
        funcionSensor: `tocando("${object}")`,
        esBool: true
      }
      this.blocksGallery.crearBloqueSensor(name, undefined, properties)
    }
  },

  loadProyect(challenge) {
    this.store.createRecord("desafio", challenge);
    this.router.transitionTo("desafio", challenge.id);
  },

  fileInputProyecto() {
    return document.getElementById("cargarProyectoInput");
  },

  limpiarInput(input) {
    input.value = null;
  },

  actions: {
    importarProyecto() {
      this.fileInputProyecto().click();
    },
  },
});
<|endoftext|>


<commit_before>const FORM_CHAIN_LOOKUP_SELECTOR = '#ext-etheraddresslookup-chain_lookup_form';
const FORM_CHAIN_LOOKUP_INPUT_SELECTOR = '#ext-etheraddresslookup-addr_or_tx';
const FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR = '#ext-etheraddresslookup-chain_lookup_output';
const FORM_CHAIN_LOOKUP_DETAILS_SELECTOR = '#ext-etheraddresslookup-chain_lookup_details';

class ChainLookup
{
    constructor()
    {
        chrome.runtime.sendMessage({ func: "rpc_provider" }, (objResponse) => {
            this.web3 = new Web3(new Web3.providers.HttpProvider(objResponse.resp));
            this.setupFormSubmitHandler();
        });
    }

    setupFormSubmitHandler()
    {
        if(document.querySelector(FORM_CHAIN_LOOKUP_SELECTOR)) {

            this.getRpcDetails();

            document.querySelector(FORM_CHAIN_LOOKUP_SELECTOR).addEventListener('submit', async (event) => {
                event.preventDefault();

                chrome.runtime.sendMessage({ func: "blockchain_explorer" }, async function(objResponse) {
                    this.strBlockExplorer = objResponse.resp;

                    const strInput = document.querySelector(FORM_CHAIN_LOOKUP_INPUT_SELECTOR).value;
                    await this.resolve(strInput);
                }.bind(this));
            });
        }
    }

    async getRpcDetails()
    {

        if(document.querySelector(FORM_CHAIN_LOOKUP_DETAILS_SELECTOR)) {
            let intBlockNumber = await this.web3.eth.blockNumber;

            this.intBlockNumber = intBlockNumber;

            document.querySelector(FORM_CHAIN_LOOKUP_DETAILS_SELECTOR).innerHTML = `
                Latest Block Number: ${intBlockNumber.toLocaleString("en-US")}

            `

            chrome.runtime.sendMessage({ func: "rpc_details" }, (objResponse) => {
                let objDetails = JSON.parse(objResponse.resp);

                document.querySelector(FORM_CHAIN_LOOKUP_DETAILS_SELECTOR).innerHTML += `
                    Network: ${objDetails.name} (${objDetails.type})
                `;
            });
        }
    }

    async resolve(strInput)
    {
        strInput = strInput.trim();
        strInput = strInput.startsWith("0x") ? strInput : `0x${strInput}`

        document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = ``;
        let strInputType = this.getInputType(strInput);

        switch(strInputType.toUpperCase()) {
            default:
            case 'ADDRESS':
                await this.doAddressLookup(strInput);
                break;
            case 'TX':
                this.doTxLookup(strInput);
                break;
            case 'UNKNOWN':
                if(strInput.endsWith(".eth")) {
                    document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML += `
                    ENS Integration coming soon. Search by 0x address or transaction hash.
                `;
                } else {
                    document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                    Invalid input. Either an Ethereum address or transaction hash.
                `;
                }
            break;
        }
    }

    getInputType(strInput)
    {
        /**
         * Determines if we are inptting an address or transaction hash
         */
        switch(strInput.length) {
            case 42 : //Address
                return "ADDRESS";
            case 66 : //TX HASH
                return "TX";
            default :
                return "UNKNOWN";
        }
    }

    async doAddressLookup(strInput)
    {
        let strAddressBlockie = blockies.create({
            seed: strInput.toLowerCase(),
            size: 8,
            scale: 16
        }).toDataURL();

        let objAddressDetails = {
            "eth": 0,
            "tx": 0,
            "contract": false
        };

        objAddressDetails.eth = this.web3.fromWei(
            this.web3.eth.getBalance(strInput).toString(10), "ether"
        ).toLocaleString("en-US", {maximumSignificantDigits: 5});

        objAddressDetails.tx = parseInt(this.web3.eth.getTransactionCount(strInput)).toLocaleString();
        objAddressDetails.contract = this.web3.eth.getCode(strInput) === "0x" ? "Normal (EOA)": "Contract";

        let strAddressLookedup = strInput;
        let objLabels = new Labels();
        let objLabelledAddress = await objLabels.getLabelForAddress(strAddressLookedup);
        if(typeof objLabelledAddress !== "undefined") {
            let strLabel = `${objLabelledAddress.label} (${ChainLookup.getShortAddress(strAddressLookedup)})`;
            strAddressLookedup = objLabels.getTemplate(strLabel, objLabelledAddress.color);
        }

        document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                Address:

                        ${strAddressLookedup}

ETH: ${objAddressDetails.eth}

Transactions out: ${objAddressDetails.tx}

Type: ${objAddressDetails.contract}

            `;
    }

    async doTxLookup(strInput)
    {
        let objTransaction = await this.web3.eth.getTransaction(strInput);

        if(objTransaction === null) {
            document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                Unknown transaction (it might still be pending, waiting to be mined by the network)
            `;
            return;
        }

        let objTransactionReceipt = await this.web3.eth.getTransactionReceipt(strInput);

        if(objTransactionReceipt === null) {
            objTransactionReceipt = {
                contractAddress: null,
                gasUsed: null,
                is_pending: true
            }
        }

        let strAddressFromBlockie = blockies.create({
            seed: objTransaction.from.toLowerCase(),
            size: 8,
            scale: 16
        }).toDataURL();

        let strToAddress = objTransaction.to !== null ? objTransaction.to : objTransactionReceipt.contractAddress;
        let strAddressToBlockie = blockies.create({
            seed: strToAddress.toLowerCase(),
            size: 8,
            scale: 16
        }).toDataURL();

        let objTxDetails = {
            "eth": this.web3.fromWei(objTransaction.value).toLocaleString("en-US", {maximumSignificantDigits: 4}),
            "eth_full": this.web3.fromWei(objTransaction.value).toLocaleString("en-US", {maximumSignificantDigits: 18}),
            "gas": {
                "limit": objTransaction.gas.toLocaleString("en-US"),
                "price": this.web3.fromWei(objTransaction.gasPrice.toString()).toLocaleString("en-US"),
                "used": objTransactionReceipt.gasUsed,
                "used_percent": (objTransactionReceipt.gasUsed / objTransaction.gas)*100,
                "fee": (objTransactionReceipt.gasUsed * this.web3.fromWei(objTransaction.gasPrice.toString()).toLocaleString("en-US"))
            },
            "block": {
                "number": objTransaction.blockNumber ? objTransaction.blockNumber.toLocaleString("en-US") : null,
                "ago": (this.intBlockNumber - objTransaction.blockNumber).toLocaleString("en-US"),
            },
            "nonce": objTransaction.nonce.toLocaleString("en-US"),
            "input": objTransaction.v,
            "contract": {
                "is": false
            }
        }

        objTxDetails.contract.is = this.web3.eth.getCode(strToAddress) === "0x" ? false : true;

        let objLabels = new Labels();
        let objLabelledAddress;
        let strLabel;
        let blUseLables = false;
        let objLabelledAddresses = {
            "from": ChainLookup.getShortAddress(objTransaction.from),
            "to": ChainLookup.getShortAddress(strToAddress)
        }

        objLabelledAddress = await objLabels.getLabelForAddress(objTransaction.from);
        if(typeof objLabelledAddress !== "undefined") {
            strLabel = `${objLabelledAddress.label} (${ChainLookup.getShortAddress(objTransaction.from)})`;
            objLabelledAddresses.from = objLabels.getTemplate(strLabel, objLabelledAddress.color);
            blUseLables = true;
        }

        objLabelledAddress = await objLabels.getLabelForAddress(strToAddress);
        if(typeof objLabelledAddress !== "undefined") {
            strLabel = `${objLabelledAddress.label} (${ChainLookup.getShortAddress(strToAddress)})`;
            objLabelledAddresses.to = objLabels.getTemplate(strLabel, objLabelledAddress.color);
            blUseLables = true;
        }

        document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                Transaction:

                        ${objTransactionReceipt.contractAddress !== null
                            ? `<strong>🐣 This transaction created a contract</strong><br />`
                            : objTxDetails.contract.is
                                ? `<strong>📞 This transaction called a contract</strong><br />`
                                : ``
                        }

                            ${objLabelledAddresses.from}

                        ${blUseLables ? `<br />↓<br />` : `→`}
                        ${objTxDetails.eth} ETH
                        ${blUseLables ? `<br />↓<br />` : `→`}

                            ${objLabelledAddresses.to}

                        ${
                            objTransactionReceipt.is_pending
                                ?
                                    `<span style="color:#C0BC37;font-weight:600;">Transaction is pending</span>`
                                :
                                    objTransactionReceipt.status === "0x0"
                                        ?
                                            `<span style="color:#C03737;font-weight:600;">Transaction Failed</span>`
                                        :
                                        `<span style="color:#4FC037;font-weight:600;">Transaction Successful</span>`
                        }

Gas Limit: ${objTxDetails.gas.limit}
                        ${
                            objTransactionReceipt.is_pending
                                ?
                                    ``
                                :
                                    `(${Math.ceil(objTxDetails.gas.used_percent)}% consumed)`
                        }

Transaction Fee: Ξ${objTxDetails.gas.fee}
                            ${objTransactionReceipt.contractAddress === null
                                && objTxDetails.gas.fee >= objTxDetails.eth
                                && objTxDetails.eth > 0
                                ? `<a title="Yikes! It cost more to send">😱</a>`
                                : ``}

Block:
                        ${
                            objTransactionReceipt.is_pending
                                ?
                                    `<i>Pending</i>`
                                :
                                    `${objTxDetails.block.number} (${objTxDetails.block.ago.replace(",", "") <= 3000000 ? objTxDetails.block.ago + " blocks ago" : "millions of blocks ago"})`
                        }

            `;
    }

    static getShortAddress(strAddress)
    {
        return `${strAddress.slice(0, 4)}...${strAddress.slice(strAddress.length-4, strAddress.length)}`
    }
} <commit_msg>Added notice about being on the wrong network<commit_after>const FORM_CHAIN_LOOKUP_SELECTOR = '#ext-etheraddresslookup-chain_lookup_form';
const FORM_CHAIN_LOOKUP_INPUT_SELECTOR = '#ext-etheraddresslookup-addr_or_tx';
const FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR = '#ext-etheraddresslookup-chain_lookup_output';
const FORM_CHAIN_LOOKUP_DETAILS_SELECTOR = '#ext-etheraddresslookup-chain_lookup_details';

class ChainLookup
{
    constructor()
    {
        chrome.runtime.sendMessage({ func: "rpc_provider" }, (objResponse) => {
            this.web3 = new Web3(new Web3.providers.HttpProvider(objResponse.resp));
            this.setupFormSubmitHandler();
        });
    }

    setupFormSubmitHandler()
    {
        if(document.querySelector(FORM_CHAIN_LOOKUP_SELECTOR)) {

            this.getRpcDetails();

            document.querySelector(FORM_CHAIN_LOOKUP_SELECTOR).addEventListener('submit', async (event) => {
                event.preventDefault();

                chrome.runtime.sendMessage({ func: "blockchain_explorer" }, async function(objResponse) {
                    this.strBlockExplorer = objResponse.resp;

                    const strInput = document.querySelector(FORM_CHAIN_LOOKUP_INPUT_SELECTOR).value;
                    await this.resolve(strInput);
                }.bind(this));
            });
        }
    }

    async getRpcDetails()
    {

        if(document.querySelector(FORM_CHAIN_LOOKUP_DETAILS_SELECTOR)) {
            let intBlockNumber = await this.web3.eth.blockNumber;

            this.intBlockNumber = intBlockNumber;

            document.querySelector(FORM_CHAIN_LOOKUP_DETAILS_SELECTOR).innerHTML = `
                Latest Block Number: ${intBlockNumber.toLocaleString("en-US")}

            `

            chrome.runtime.sendMessage({ func: "rpc_details" }, (objResponse) => {
                let objDetails = JSON.parse(objResponse.resp);

                document.querySelector(FORM_CHAIN_LOOKUP_DETAILS_SELECTOR).innerHTML += `
                    Network: ${objDetails.name} (${objDetails.type})
                `;
            });
        }
    }

    async resolve(strInput)
    {
        strInput = strInput.trim();
        strInput = strInput.startsWith("0x") ? strInput : `0x${strInput}`

        document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = ``;
        let strInputType = this.getInputType(strInput);

        switch(strInputType.toUpperCase()) {
            default:
            case 'ADDRESS':
                await this.doAddressLookup(strInput);
                break;
            case 'TX':
                this.doTxLookup(strInput);
                break;
            case 'UNKNOWN':
                if(strInput.endsWith(".eth")) {
                    document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML += `
                    ENS Integration coming soon. Search by 0x address or transaction hash.
                `;
                } else {
                    document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                    Invalid input. Either an Ethereum address or transaction hash.
                `;
                }
            break;
        }
    }

    getInputType(strInput)
    {
        /**
         * Determines if we are inptting an address or transaction hash
         */
        switch(strInput.length) {
            case 42 : //Address
                return "ADDRESS";
            case 66 : //TX HASH
                return "TX";
            default :
                return "UNKNOWN";
        }
    }

    async doAddressLookup(strInput)
    {
        let strAddressBlockie = blockies.create({
            seed: strInput.toLowerCase(),
            size: 8,
            scale: 16
        }).toDataURL();

        let objAddressDetails = {
            "eth": 0,
            "tx": 0,
            "contract": false
        };

        objAddressDetails.eth = this.web3.fromWei(
            this.web3.eth.getBalance(strInput).toString(10), "ether"
        ).toLocaleString("en-US", {maximumSignificantDigits: 5});

        objAddressDetails.tx = parseInt(this.web3.eth.getTransactionCount(strInput)).toLocaleString();
        objAddressDetails.contract = this.web3.eth.getCode(strInput) === "0x" ? "Normal (EOA)": "Contract";

        let strAddressLookedup = strInput;
        let objLabels = new Labels();
        let objLabelledAddress = await objLabels.getLabelForAddress(strAddressLookedup);
        if(typeof objLabelledAddress !== "undefined") {
            let strLabel = `${objLabelledAddress.label} (${ChainLookup.getShortAddress(strAddressLookedup)})`;
            strAddressLookedup = objLabels.getTemplate(strLabel, objLabelledAddress.color);
        }

        document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                Address:

                        ${strAddressLookedup}

ETH: ${objAddressDetails.eth}

Transactions out: ${objAddressDetails.tx}

Type: ${objAddressDetails.contract}

            `;
    }

    async doTxLookup(strInput)
    {
        let objTransaction = await this.web3.eth.getTransaction(strInput);

        if(objTransaction === null) {
            document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                Unknown transaction (it might still be pending, waiting to be mined by the network or you may be on the wrong network)
            `;
            return;
        }

        let objTransactionReceipt = await this.web3.eth.getTransactionReceipt(strInput);

        if(objTransactionReceipt === null) {
            objTransactionReceipt = {
                contractAddress: null,
                gasUsed: null,
                is_pending: true
            }
        }

        let strAddressFromBlockie = blockies.create({
            seed: objTransaction.from.toLowerCase(),
            size: 8,
            scale: 16
        }).toDataURL();

        let strToAddress = objTransaction.to !== null ? objTransaction.to : objTransactionReceipt.contractAddress;
        let strAddressToBlockie = blockies.create({
            seed: strToAddress.toLowerCase(),
            size: 8,
            scale: 16
        }).toDataURL();

        let objTxDetails = {
            "eth": this.web3.fromWei(objTransaction.value).toLocaleString("en-US", {maximumSignificantDigits: 4}),
            "eth_full": this.web3.fromWei(objTransaction.value).toLocaleString("en-US", {maximumSignificantDigits: 18}),
            "gas": {
                "limit": objTransaction.gas.toLocaleString("en-US"),
                "price": this.web3.fromWei(objTransaction.gasPrice.toString()).toLocaleString("en-US"),
                "used": objTransactionReceipt.gasUsed,
                "used_percent": (objTransactionReceipt.gasUsed / objTransaction.gas)*100,
                "fee": (objTransactionReceipt.gasUsed * this.web3.fromWei(objTransaction.gasPrice.toString()).toLocaleString("en-US"))
            },
            "block": {
                "number": objTransaction.blockNumber ? objTransaction.blockNumber.toLocaleString("en-US") : null,
                "ago": (this.intBlockNumber - objTransaction.blockNumber).toLocaleString("en-US"),
            },
            "nonce": objTransaction.nonce.toLocaleString("en-US"),
            "input": objTransaction.v,
            "contract": {
                "is": false
            }
        }

        objTxDetails.contract.is = this.web3.eth.getCode(strToAddress) === "0x" ? false : true;

        let objLabels = new Labels();
        let objLabelledAddress;
        let strLabel;
        let blUseLables = false;
        let objLabelledAddresses = {
            "from": ChainLookup.getShortAddress(objTransaction.from),
            "to": ChainLookup.getShortAddress(strToAddress)
        }

        objLabelledAddress = await objLabels.getLabelForAddress(objTransaction.from);
        if(typeof objLabelledAddress !== "undefined") {
            strLabel = `${objLabelledAddress.label} (${ChainLookup.getShortAddress(objTransaction.from)})`;
            objLabelledAddresses.from = objLabels.getTemplate(strLabel, objLabelledAddress.color);
            blUseLables = true;
        }

        objLabelledAddress = await objLabels.getLabelForAddress(strToAddress);
        if(typeof objLabelledAddress !== "undefined") {
            strLabel = `${objLabelledAddress.label} (${ChainLookup.getShortAddress(strToAddress)})`;
            objLabelledAddresses.to = objLabels.getTemplate(strLabel, objLabelledAddress.color);
            blUseLables = true;
        }

        document.querySelector(FORM_CHAIN_LOOKUP_OUTPUT_SELECTOR).innerHTML = `
                Transaction:

                        ${objTransactionReceipt.contractAddress !== null
                            ? `<strong>🐣 This transaction created a contract</strong><br />`
                            : objTxDetails.contract.is
                                ? `<strong>📞 This transaction called a contract</strong><br />`
                                : ``
                        }

                            ${objLabelledAddresses.from}

                        ${blUseLables ? `<br />↓<br />` : `→`}
                        ${objTxDetails.eth} ETH
                        ${blUseLables ? `<br />↓<br />` : `→`}

                            ${objLabelledAddresses.to}

                        ${
                            objTransactionReceipt.is_pending
                                ?
                                    `<span style="color:#C0BC37;font-weight:600;">Transaction is pending</span>`
                                :
                                    objTransactionReceipt.status === "0x0"
                                        ?
                                            `<span style="color:#C03737;font-weight:600;">Transaction Failed</span>`
                                        :
                                        `<span style="color:#4FC037;font-weight:600;">Transaction Successful</span>`
                        }

Gas Limit: ${objTxDetails.gas.limit}
                        ${
                            objTransactionReceipt.is_pending
                                ?
                                    ``
                                :
                                    `(${Math.ceil(objTxDetails.gas.used_percent)}% consumed)`
                        }

Transaction Fee: Ξ${objTxDetails.gas.fee}
                            ${objTransactionReceipt.contractAddress === null
                                && objTxDetails.gas.fee >= objTxDetails.eth
                                && objTxDetails.eth > 0
                                ? `<a title="Yikes! It cost more to send">😱</a>`
                                : ``}

Block:
                        ${
                            objTransactionReceipt.is_pending
                                ?
                                    `<i>Pending</i>`
                                :
                                    `${objTxDetails.block.number} (${objTxDetails.block.ago.replace(",", "") <= 3000000 ? objTxDetails.block.ago + " blocks ago" : "millions of blocks ago"})`
                        }

            `;
    }

    static getShortAddress(strAddress)
    {
        return `${strAddress.slice(0, 4)}...${strAddress.slice(strAddress.length-4, strAddress.length)}`
    }
} <|endoftext|>