<commit_before>'use strict';

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

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

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

module.exports = class exmo extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'exmo',
            'name': 'EXMO',
            'countries': [ 'ES', 'RU' ], // Spain, Russia
            'rateLimit': 1000, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second
            'version': 'v1',
            'has': {
                'CORS': false,
                'fetchOrderBooks': true,
                'fetchTickers': true,
                'withdraw': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg',
                'api': 'https://api.exmo.com',
                'www': 'https://exmo.me',
                'doc': [
                    'https://exmo.me/en/api_doc',
                    'https://github.com/exmo-dev/exmo_api_lib/tree/master/nodejs',
                ],
                'fees': 'https://exmo.com/en/docs/fees',
            },
            'api': {
                'public': {
                    'get': [
                        'currency',
                        'order_book',
                        'pair_settings',
                        'ticker',
                        'trades',
                    ],
                },
                'private': {
                    'post': [
                        'user_info',
                        'order_create',
                        'order_cancel',
                        'user_open_orders',
                        'user_trades',
                        'user_cancelled_orders',
                        'order_trades',
                        'required_amount',
                        'deposit_address',
                        'withdraw_crypt',
                        'withdraw_get_txid',
                        'excode_create',
                        'excode_load',
                        'wallet_history',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'withdraw': {
                        'BTC': 0.001,
                        'LTC': 0.01,
                        'DOGE': 1,
                        'DASH': 0.01,
                        'ETH': 0.01,
                        'WAVES': 0.001,
                        'ZEC': 0.001,
                        'USDT': 25,
                        'XMR': 0.05,
                        'XRP': 0.02,
                        'KICK': 350,
                        'ETC': 0.01,
                        'BCH': 0.001,
                    },
                    'deposit': {
                        'USDT': 15,
                        'KICK': 50,
                    },
                },
            },
        });
    }

    async fetchMarkets () {
        let markets = await this.publicGetPairSettings ();
        let keys = Object.keys (markets);
        let result = [];
        for (let p = 0; p < keys.length; p++) {
            let id = keys[p];
            let market = markets[id];
            let symbol = id.replace ('_', '/');
            let [ base, quote ] = symbol.split ('/');
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'limits': {
                    'amount': {
                        'min': market['min_quantity'],
                        'max': market['max_quantity'],
                    },
                    'price': {
                        'min': market['min_price'],
                        'max': market['max_price'],
                    },
                    'cost': {
                        'min': market['min_amount'],
                        'max': market['max_amount'],
                    },
                },
                'precision': {
                    'amount': 8,
                    'price': 8,
                },
                'info': market,
            });
        }
        return result;
    }

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

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetOrderBook (this.extend ({
            'pair': market['id'],
        }, params));
        let result = response[market['id']];
        let orderbook = this.parseOrderBook (result, undefined, 'bid', 'ask');
        return this.extend (orderbook, {
            'bids': this.sortBy (orderbook['bids'], 0, true),
            'asks': this.sortBy (orderbook['asks'], 0),
        });
    }

    async fetchOrderBooks (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let orderBooksResult = [];
        if (typeof symbols === 'undefined')
            symbols = this.symbols;
        let fetchPairString = this.parseSymbolOrderBooksString (symbols);
        let response = await this.publicGetOrderBook (this.extend ({
            'pair': fetchPairString,
        }, params));
        // the next line is not portable
        let orderbooks = Object.values (response);
        let keys = Object.keys (response);
        for (let j = 0; j < orderbooks.length; j++) {
            let key = keys[j];
            let orderbook = this.parseOrderBook (orderbooks[j], undefined, 'bid', 'ask', '0', '1');
            orderBooksResult.push (this.extend (orderbook, {
                'symbol': key.replace ('_', '/'),
            }));
        }
        return orderbooks;
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['updated'] * 1000;
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['high']),
            'low': parseFloat (ticker['low']),
            'bid': parseFloat (ticker['buy_price']),
            'ask': parseFloat (ticker['sell_price']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last_trade']),
            'change': undefined,
            'percentage': undefined,
            'average': parseFloat (ticker['avg']),
            'baseVolume': parseFloat (ticker['vol']),
            'quoteVolume': parseFloat (ticker['vol_curr']),
            'info': ticker,
        };
    }

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

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

    parseTrade (trade, market) {
        let timestamp = trade['date'] * 1000;
        return {
            'id': trade['trade_id'].toString (),
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'order': undefined,
            'type': undefined,
            'side': trade['type'],
            'price': parseFloat (trade['price']),
            'amount': parseFloat (trade['quantity']),
        };
    }

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

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let prefix = '';
        if (type === 'market')
            prefix = 'market_';
        if (typeof price === 'undefined')
            price = 0;
        let order = {
            'pair': this.marketId (symbol),
            'quantity': amount,
            'price': price,
            'type': prefix + side,
        };
        let response = await this.privatePostOrderCreate (this.extend (order, params));
        return {
            'info': response,
            'id': response['order_id'].toString (),
        };
    }

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

    async withdraw (currency, amount, address, tag = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'amount': amount,
            'currency': currency,
            'address': address,
        };
        if (typeof tag !== 'undefined')
            request['invoice'] = tag;
        let result = await this.privatePostWithdrawCrypt (this.extend (request, params));
        return {
            'info': result,
            'id': result['task_id'],
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + path;
        if (api === 'public') {
            if (Object.keys (params).length)
                url += '?' + this.urlencode (params);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            body = this.urlencode (this.extend ({ 'nonce': nonce }, params));
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Key': this.apiKey,
                'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'),
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('result' in response) {
            if (response['result'])
                return response;
            throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        return response;
    }
};
<commit_msg>exmo fetchOrderBooks #1050<commit_after>'use strict';

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

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

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

module.exports = class exmo extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'exmo',
            'name': 'EXMO',
            'countries': [ 'ES', 'RU' ], // Spain, Russia
            'rateLimit': 1000, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second
            'version': 'v1',
            'has': {
                'CORS': false,
                'fetchOrderBooks': true,
                'fetchTickers': true,
                'withdraw': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg',
                'api': 'https://api.exmo.com',
                'www': 'https://exmo.me',
                'doc': [
                    'https://exmo.me/en/api_doc',
                    'https://github.com/exmo-dev/exmo_api_lib/tree/master/nodejs',
                ],
                'fees': 'https://exmo.com/en/docs/fees',
            },
            'api': {
                'public': {
                    'get': [
                        'currency',
                        'order_book',
                        'pair_settings',
                        'ticker',
                        'trades',
                    ],
                },
                'private': {
                    'post': [
                        'user_info',
                        'order_create',
                        'order_cancel',
                        'user_open_orders',
                        'user_trades',
                        'user_cancelled_orders',
                        'order_trades',
                        'required_amount',
                        'deposit_address',
                        'withdraw_crypt',
                        'withdraw_get_txid',
                        'excode_create',
                        'excode_load',
                        'wallet_history',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'withdraw': {
                        'BTC': 0.001,
                        'LTC': 0.01,
                        'DOGE': 1,
                        'DASH': 0.01,
                        'ETH': 0.01,
                        'WAVES': 0.001,
                        'ZEC': 0.001,
                        'USDT': 25,
                        'XMR': 0.05,
                        'XRP': 0.02,
                        'KICK': 350,
                        'ETC': 0.01,
                        'BCH': 0.001,
                    },
                    'deposit': {
                        'USDT': 15,
                        'KICK': 50,
                    },
                },
            },
        });
    }

    async fetchMarkets () {
        let markets = await this.publicGetPairSettings ();
        let keys = Object.keys (markets);
        let result = [];
        for (let p = 0; p < keys.length; p++) {
            let id = keys[p];
            let market = markets[id];
            let symbol = id.replace ('_', '/');
            let [ base, quote ] = symbol.split ('/');
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'limits': {
                    'amount': {
                        'min': market['min_quantity'],
                        'max': market['max_quantity'],
                    },
                    'price': {
                        'min': market['min_price'],
                        'max': market['max_price'],
                    },
                    'cost': {
                        'min': market['min_amount'],
                        'max': market['max_amount'],
                    },
                },
                'precision': {
                    'amount': 8,
                    'price': 8,
                },
                'info': market,
            });
        }
        return result;
    }

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

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetOrderBook (this.extend ({
            'pair': market['id'],
        }, params));
        let result = response[market['id']];
        let orderbook = this.parseOrderBook (result, undefined, 'bid', 'ask');
        return this.extend (orderbook, {
            'bids': this.sortBy (orderbook['bids'], 0, true),
            'asks': this.sortBy (orderbook['asks'], 0),
        });
    }

    async fetchOrderBooks (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let ids = undefined;
        if (!symbols) {
            ids = this.ids.join (',');
            // max URL length is 2083 symbols, including http schema, hostname, tld, etc...
            if (ids.length > 2048) {
                let numIds = this.ids.length;
                throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks');
            }
        } else {
            ids = this.marketIds (symbols);
            ids = ids.join (',');
        }
        let response = await this.publicGetOrderBook (this.extend ({
            'pair': ids,
        }, params));
        let result = {};
        ids = Object.keys (response);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let symbol = id;
            if (id in this.marketsById) {
                let market = this.marketsById[id];
                symbol = market['symbol'];
            }
            result[symbol] = this.parseOrderBook (response[id], undefined, 'bid', 'ask');
        }
        return result;
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['updated'] * 1000;
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['high']),
            'low': parseFloat (ticker['low']),
            'bid': parseFloat (ticker['buy_price']),
            'ask': parseFloat (ticker['sell_price']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last_trade']),
            'change': undefined,
            'percentage': undefined,
            'average': parseFloat (ticker['avg']),
            'baseVolume': parseFloat (ticker['vol']),
            'quoteVolume': parseFloat (ticker['vol_curr']),
            'info': ticker,
        };
    }

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

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

    parseTrade (trade, market) {
        let timestamp = trade['date'] * 1000;
        return {
            'id': trade['trade_id'].toString (),
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'order': undefined,
            'type': undefined,
            'side': trade['type'],
            'price': parseFloat (trade['price']),
            'amount': parseFloat (trade['quantity']),
        };
    }

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

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let prefix = '';
        if (type === 'market')
            prefix = 'market_';
        if (typeof price === 'undefined')
            price = 0;
        let order = {
            'pair': this.marketId (symbol),
            'quantity': amount,
            'price': price,
            'type': prefix + side,
        };
        let response = await this.privatePostOrderCreate (this.extend (order, params));
        return {
            'info': response,
            'id': response['order_id'].toString (),
        };
    }

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

    async withdraw (currency, amount, address, tag = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'amount': amount,
            'currency': currency,
            'address': address,
        };
        if (typeof tag !== 'undefined')
            request['invoice'] = tag;
        let result = await this.privatePostWithdrawCrypt (this.extend (request, params));
        return {
            'info': result,
            'id': result['task_id'],
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + path;
        if (api === 'public') {
            if (Object.keys (params).length)
                url += '?' + this.urlencode (params);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            body = this.urlencode (this.extend ({ 'nonce': nonce }, params));
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Key': this.apiKey,
                'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'),
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('result' in response) {
            if (response['result'])
                return response;
            throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        return response;
    }
};
<|endoftext|>


<commit_before>/* globals VIZI: false, Q: false, _: false, THREE: false, d3: false, geoJSONArea: false, $: false,
          TWEEN: false */
'use strict';

// (function(){
VIZI.ENABLE_OUTLINES = false;
VIZI.ENABLE_ROADS = true;

VIZI.DEBUG = false;
function THF(){
    VIZI.City.call(this);
}
THF.prototype = new VIZI.City();
THF.prototype.constructor = VIZI.City;

THF.prototype.init = function(options) {
  VIZI.Log('Loading city');

  var startTime = Date.now();
  var self = this;

  var deferred = Q.defer();

  if (!options) {
    options = {};
  }

  this.options = options;

  var hash = window.location.hash.replace('#', '');
  var coordCheck = /^(\-?\d+(\.\d+)?),(\-?\d+(\.\d+)?)$/;
  if (coordCheck.test(hash)) {
    options.coords = hash.split(',').reverse();
  }

  _.defaults(options, {
    coords: [-0.01924, 51.50358],
    capZoom: true,
    capOrbit: true,
    overpass: true,
    overpassGridUpdate: true,
    overpassWayIntersect: false,
    controls: { enable: true }
  });

  // Output city options
  VIZI.Log(options);

  // Set up geo methods
  self.geo = VIZI.Geo.getInstance({
    center: options.coords
  });

  // Load city using promises

  self.publish('loadingProgress', 0);

  // Initialise loading UI
  this.initLoadingUI().then(function() {
    self.publish('loadingProgress', 0.1);

    // Initialise debug tools
    return self.initDebug();
  }).then(function() {
    self.publish('loadingProgress', 0.2);

    // Initialise WebGL
    return self.initWebGL(options);
  // }).then(function() {
  //   self.publish('loadingProgress', 0.25);

  //   // Initialise attribution UI
  //   return self.initAttributionUI();
  }).then(function() {
    self.publish('loadingProgress', 0.3);

    var promises = [];

    // Initialise DOM events
    promises.push(self.initDOMEvents());

    // Initialise controls
    promises.push(self.initControls());

    return Q.allSettled(promises);
  }).then(function() {
    self.publish('loadingProgress', 0.4);

    // Initialise grid manager
    return self.initGrid();
  }).then(function() {
    self.publish('loadingProgress', 0.5);

    // TODO: Work out a way to use progress event of each promises to increment loading progress
    // Perhaps by looping through each promises individually and working out progress fraction by num. of promises / amount processed

    // Load objects using promises
    var promises = [];

    // Load core city objects
    promises.push(self.loadCoreObjects());

    // Load data from the OSM Overpass API
    if (options.overpass) {
      self.data = new VIZI.DataOverpassCustom({
        gridUpdate: options.overpassGridUpdate
      });
      promises.push(self.loadOverpass(options.overpassWayIntersect));
    }

    return Q.allSettled(promises);
  }).then(function () {
    // Set up and start application loop
    self.loop = new VIZI.Loop();

    self.publish('loadingProgress', 1);
    self.publish('loadingComplete');

    VIZI.Log('Finished loading city in ' + (Date.now() - startTime) + 'ms');

    deferred.resolve();
  }).fail(function(error){
    throw error;
  });

  return deferred.promise;
};

var city = new THF();

var cameraOptions = {"capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[9.22, 49.15],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[860.3848352080889,440.04424654556533],"cameraRadius":7349,"theta":7,"phi":154,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000};

var wayPoints = {
 /* overview: {
    cameraOptions: _.clone(cameraOptions)
  },*/
  overview: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[9.22336, 49.159929],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-1470.438942166035,716.3654002568528],"cameraRadius":1996,"theta":252,"phi":91,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  }
  ,
  /*
  oderstrasse: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[13.398342,52.476684],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[2496.4731570779963,649.1202353895497],"cameraRadius":2000,"theta":78,"phi":65,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },

  suedring: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[13.398342,52.476684],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[388.9192562717134,2240.285129070899],"cameraRadius":2722,"theta":294.5,"phi":79,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },
  */
  esplanadenschenkel: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":5,"orbitCapHigh":175,"coords":[9.207428,49.144565],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-1470.438942166035,716.3654002568528],"cameraRadius":600,"theta":10,"phi":30,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },
  inselschenkel: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":5,"orbitCapHigh":175,"coords":[9.20742115512, 49.14852917309],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-2000,650],"cameraRadius":600,"theta":350,"phi":30,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },
  neckarschenkel: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":5,"orbitCapHigh":175,"coords":[9.21021285,49.1483174],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-2000,650],"cameraRadius":600,"theta":200,"phi":20,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  }
};

var triggerTween = function(city, wayPoint){
  console.log('start tween');
  var options = city.webgl.camera.serialize();
  var current = {theta: options.theta, phi: options.phi, target0: options.target[0], target1: options.target[1], cameraRadius: options.cameraRadius};
  var next = wayPoints[wayPoint].cameraOptions;
  var target = {theta: next.theta, phi: next.phi, target0: next.target[0], target1: next.target[1], cameraRadius: next.cameraRadius};
  var tween = new TWEEN.Tween(current).to(target, 5000).easing(TWEEN.Easing.Bounce.InOut).delay(1500);//.easing(TWEEN.Easing.Sinusoidal.EaseInOut);
  tween.onUpdate(function(){
    console.log('tween', this);
    options.target = [this.target0, this.target1];
    options.phi = this.phi;
    options.theta = this.theta;
    options.cameraRadius = this.cameraRadius;
    city.webgl.camera.load(options);
  });
  tween.start();
};

$('.section').hide();

$('.nav a').click(function(e){
  e.preventDefault();
  var action = $(this).attr('href').substring(1);
  if (wayPoints[action] !== undefined) {
    triggerTween(city, action);
  }
  $('.nav li').removeClass('active');
  $(this).parent().addClass('active');
  $('.section').hide();
  $('#' + action).show();
});

function animate() {

    requestAnimationFrame( animate ); // js/RequestAnimationFrame.js needs to be included too.
    TWEEN.update();

}

animate();

city.init({
  container: $('.webgl-container')[0],
  camera: cameraOptions,
  // coords: [-0.01924, 51.50358] // Canary Wharf
  // coords: [13.381320, 52.498460] // Tempelhofer Ufer 23
  coords: [9.22, 49.15] // Tempelhofer Feld
  // coords: [-0.1159894466, 51.5045487479] // London Eye
  // coords: [-73.984762, 40.7516199] // NYC (performance-heavy)
}).then(function(){
  // city.subscribe("update", function(){
  //   TWEEN.update();
  // });
  // var floorContainer = new THREE.Object3D();
  city.webgl.camera.load(cameraOptions);
  // var floorWireGeom = new THREE.PlaneGeometry(5000, 5000, 200, 200);
  // var floorWireMat = new THREE.MeshBasicMaterial({color: 0xeeeeee, wireframe: true});
  // var floorWire = new THREE.Mesh(floorWireGeom, floorWireMat);
  // floorWire.position.y = -0.3;
  // floorWire.rotation.x = - 90 * Math.PI / 180;

  // floorContainer.add(floorWire);

  // var floorGeom = new THREE.PlaneGeometry(40000, 40000, 4, 4);
  // var floorGeom = new THREE.CubeGeometry(50, 50, 50);
  // var floorMat = new THREE.MeshBasicMaterial({color: 0xff0000});
  // var floor = new THREE.Mesh(floorGeom, floorMat);
  // var geo = VIZI.Geo.getInstance();

  // var blue = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors,
  //     ambient: 0x0000ff,
  //     color: 0x0000ff,
  // });
  // var skyboxMesh = new THREE.Mesh( new THREE.CubeGeometry( 100000, 100000, 100000, 1, 1, 1, null, true ), blue);
  // // add it to the scene
  // city.publish('addToScene', skyboxMesh);

  // var cube = new THREE.Mesh(new THREE.CubeGeometry(50, 50, 50), new THREE.MeshLambertMaterial({color: 0xCC0000}));
  // cube.position.y = 15;
  // cube.castShadow = true;
  // city.publish('addToScene', cube);

   var buildings = [], features = [];
  var ensemble = [];

  $('#new-buildings').change(function(){
    if ($(this).prop('checked')) {
       //aufrufende Instanz:
      console.log("Trigger-aufrufende Instanz: "+this);
      var material = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors,
        //mit dieser Farbkombi lässt sich ein Unterschied zu den Blau-Farben erkennen ....
  //     ambient: 0xa9bbd6,
       color: 0x00FF00
      })
      _.each(features, function(feature){
        var obj = createExtrudedObject({
          coordinates: feature.geometry.coordinates,
          properties: _.defaults(feature.properties, {
            roof: {}
          })
        }, city.geo, material);
        buildings.push(obj);
        city.publish('addToScene', obj);
      });
    } else {
      _.each(buildings, function(obj){
        city.publish('removeFromScene', obj);
      });
      buildings = [];
    }
  });

  function go(dummy){
    var uses = ['Suedschenkel', 'Westschenkel'];
    var ensemble_colours = [0x0000FF, 0x7D7D7D];
  //  console.log(colours[1]);

    for (var i = 0; i < uses.length; i++) {
      if (dummy === uses[i]){
    //    console.log(i);
    // colours =  colours[i]
 /*   if (dummy) {
      console.log(dummy+ " kommt in Funktion an")
    };
    */
      //aufrufende Instanz:
  //    console.log("Funktion-aufrufende Instanz: "+this);
  //     var material = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors})
      var colorobj = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors,
       ambient: 0x00FFFF,
       color: ensemble_colours[i],
       opacity: 0.8
      });
       console.log(colorobj.color);
       console.log(colorobj.opacity);
      _.each(features, function(feature){
        var obj = createExtrudedObject({
          coordinates: feature.geometry.coordinates,
          properties: _.defaults(feature.properties, {
            roof: {}
          })
        }, city.geo, colorobj);
  //      console.log(obj);
        buildings.push(obj);
        city.publish('addToScene', obj);
      });

 /*   else {
      _.each(buildings, function(obj){
        city.publish('removeFromScene', obj);
      });
      buildings = [];
    } */
      }
    };
  }

//  $('#new-buildings').change(go(0));

  $.getJSON('buildings.geojson', function(geojson){
    var ensemble;
    _.each(geojson.features, function(feature){
      ensemble = feature.properties.ensemble;

        feature.properties.area = feature.properties.area || geoJSONArea(feature.geometry);
        // city.data.processArea(feature.geometry.coordinates[0]);
        if (!feature.properties.height) {
          if (feature.properties.maxfloors) {
            feature.properties.height = feature.properties.maxfloors * 3.14;
          } else {
            feature.properties.height = 22;
          }
        }
        // console.log(feature.properties);
        features.push(feature);
        if (ensemble){
          go(ensemble);
        }
        else {
          $('#new-buildings').prop('checked', true).change();
        }
//die (nicht sichtbare) Control 'Zeige Gebäude' wird ANgehakt

    });
  });
});
// }());<commit_msg>cubic tweening<commit_after>/* globals VIZI: false, Q: false, _: false, THREE: false, d3: false, geoJSONArea: false, $: false,
          TWEEN: false */
'use strict';

// (function(){
VIZI.ENABLE_OUTLINES = false;
VIZI.ENABLE_ROADS = true;

VIZI.DEBUG = false;
function THF(){
    VIZI.City.call(this);
}
THF.prototype = new VIZI.City();
THF.prototype.constructor = VIZI.City;

THF.prototype.init = function(options) {
  VIZI.Log('Loading city');

  var startTime = Date.now();
  var self = this;

  var deferred = Q.defer();

  if (!options) {
    options = {};
  }

  this.options = options;

  var hash = window.location.hash.replace('#', '');
  var coordCheck = /^(\-?\d+(\.\d+)?),(\-?\d+(\.\d+)?)$/;
  if (coordCheck.test(hash)) {
    options.coords = hash.split(',').reverse();
  }

  _.defaults(options, {
    coords: [-0.01924, 51.50358],
    capZoom: true,
    capOrbit: true,
    overpass: true,
    overpassGridUpdate: true,
    overpassWayIntersect: false,
    controls: { enable: true }
  });

  // Output city options
  VIZI.Log(options);

  // Set up geo methods
  self.geo = VIZI.Geo.getInstance({
    center: options.coords
  });

  // Load city using promises

  self.publish('loadingProgress', 0);

  // Initialise loading UI
  this.initLoadingUI().then(function() {
    self.publish('loadingProgress', 0.1);

    // Initialise debug tools
    return self.initDebug();
  }).then(function() {
    self.publish('loadingProgress', 0.2);

    // Initialise WebGL
    return self.initWebGL(options);
  // }).then(function() {
  //   self.publish('loadingProgress', 0.25);

  //   // Initialise attribution UI
  //   return self.initAttributionUI();
  }).then(function() {
    self.publish('loadingProgress', 0.3);

    var promises = [];

    // Initialise DOM events
    promises.push(self.initDOMEvents());

    // Initialise controls
    promises.push(self.initControls());

    return Q.allSettled(promises);
  }).then(function() {
    self.publish('loadingProgress', 0.4);

    // Initialise grid manager
    return self.initGrid();
  }).then(function() {
    self.publish('loadingProgress', 0.5);

    // TODO: Work out a way to use progress event of each promises to increment loading progress
    // Perhaps by looping through each promises individually and working out progress fraction by num. of promises / amount processed

    // Load objects using promises
    var promises = [];

    // Load core city objects
    promises.push(self.loadCoreObjects());

    // Load data from the OSM Overpass API
    if (options.overpass) {
      self.data = new VIZI.DataOverpassCustom({
        gridUpdate: options.overpassGridUpdate
      });
      promises.push(self.loadOverpass(options.overpassWayIntersect));
    }

    return Q.allSettled(promises);
  }).then(function () {
    // Set up and start application loop
    self.loop = new VIZI.Loop();

    self.publish('loadingProgress', 1);
    self.publish('loadingComplete');

    VIZI.Log('Finished loading city in ' + (Date.now() - startTime) + 'ms');

    deferred.resolve();
  }).fail(function(error){
    throw error;
  });

  return deferred.promise;
};

var city = new THF();

var cameraOptions = {"capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[9.22, 49.15],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[860.3848352080889,440.04424654556533],"cameraRadius":7349,"theta":7,"phi":154,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000};

var wayPoints = {
 /* overview: {
    cameraOptions: _.clone(cameraOptions)
  },*/
  overview: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[9.22336, 49.159929],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-1470.438942166035,716.3654002568528],"cameraRadius":1996,"theta":252,"phi":91,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  }
  ,
  /*
  oderstrasse: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[13.398342,52.476684],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[2496.4731570779963,649.1202353895497],"cameraRadius":2000,"theta":78,"phi":65,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },

  suedring: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":65,"orbitCapHigh":175,"coords":[13.398342,52.476684],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[388.9192562717134,2240.285129070899],"cameraRadius":2722,"theta":294.5,"phi":79,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },
  */
  esplanadenschenkel: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":5,"orbitCapHigh":175,"coords":[9.207428,49.144565],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-1470.438942166035,716.3654002568528],"cameraRadius":600,"theta":10,"phi":30,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },
  inselschenkel: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":5,"orbitCapHigh":175,"coords":[9.20742115512, 49.14852917309],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-2000,650],"cameraRadius":600,"theta":350,"phi":30,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  },
  neckarschenkel: {
    cameraOptions: {
      "capZoom":true,"capOrbit":true,"orbitCapLow":5,"orbitCapHigh":175,"coords":[9.21021285,49.1483174],"overpassGridUpdate":true,"overpassWayIntersect":false,"controls":{"enable":true},"target":[-2000,650],"cameraRadius":600,"theta":200,"phi":20,"zoomCapLow":250,"zoomCapHigh":2000,"cameraFov":40,"near":2,"far":40000
    }
  }
};

var triggerTween = function(city, wayPoint){
  console.log('start tween');
  var options = city.webgl.camera.serialize();
  var current = {theta: options.theta, phi: options.phi, target0: options.target[0], target1: options.target[1], cameraRadius: options.cameraRadius};
  var next = wayPoints[wayPoint].cameraOptions;
  var target = {theta: next.theta, phi: next.phi, target0: next.target[0], target1: next.target[1], cameraRadius: next.cameraRadius};
  var tween = new TWEEN.Tween(current).to(target, 5000).easing(TWEEN.Easing.Cubic.InOut).delay(500);//.easing(TWEEN.Easing.Sinusoidal.EaseInOut);
  tween.onUpdate(function(){
    console.log('tween', this);
    options.target = [this.target0, this.target1];
    options.phi = this.phi;
    options.theta = this.theta;
    options.cameraRadius = this.cameraRadius;
    city.webgl.camera.load(options);
  });
  tween.start();
};

$('.section').hide();

$('.nav a').click(function(e){
  e.preventDefault();
  var action = $(this).attr('href').substring(1);
  if (wayPoints[action] !== undefined) {
    triggerTween(city, action);
  }
  $('.nav li').removeClass('active');
  $(this).parent().addClass('active');
  $('.section').hide();
  $('#' + action).show();
});

function animate() {

    requestAnimationFrame( animate ); // js/RequestAnimationFrame.js needs to be included too.
    TWEEN.update();

}

animate();

city.init({
  container: $('.webgl-container')[0],
  camera: cameraOptions,
  // coords: [-0.01924, 51.50358] // Canary Wharf
  // coords: [13.381320, 52.498460] // Tempelhofer Ufer 23
  coords: [9.22, 49.15] // Tempelhofer Feld
  // coords: [-0.1159894466, 51.5045487479] // London Eye
  // coords: [-73.984762, 40.7516199] // NYC (performance-heavy)
}).then(function(){
  // city.subscribe("update", function(){
  //   TWEEN.update();
  // });
  // var floorContainer = new THREE.Object3D();
  city.webgl.camera.load(cameraOptions);
  // var floorWireGeom = new THREE.PlaneGeometry(5000, 5000, 200, 200);
  // var floorWireMat = new THREE.MeshBasicMaterial({color: 0xeeeeee, wireframe: true});
  // var floorWire = new THREE.Mesh(floorWireGeom, floorWireMat);
  // floorWire.position.y = -0.3;
  // floorWire.rotation.x = - 90 * Math.PI / 180;

  // floorContainer.add(floorWire);

  // var floorGeom = new THREE.PlaneGeometry(40000, 40000, 4, 4);
  // var floorGeom = new THREE.CubeGeometry(50, 50, 50);
  // var floorMat = new THREE.MeshBasicMaterial({color: 0xff0000});
  // var floor = new THREE.Mesh(floorGeom, floorMat);
  // var geo = VIZI.Geo.getInstance();

  // var blue = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors,
  //     ambient: 0x0000ff,
  //     color: 0x0000ff,
  // });
  // var skyboxMesh = new THREE.Mesh( new THREE.CubeGeometry( 100000, 100000, 100000, 1, 1, 1, null, true ), blue);
  // // add it to the scene
  // city.publish('addToScene', skyboxMesh);

  // var cube = new THREE.Mesh(new THREE.CubeGeometry(50, 50, 50), new THREE.MeshLambertMaterial({color: 0xCC0000}));
  // cube.position.y = 15;
  // cube.castShadow = true;
  // city.publish('addToScene', cube);

   var buildings = [], features = [];
  var ensemble = [];

  $('#new-buildings').change(function(){
    if ($(this).prop('checked')) {
       //aufrufende Instanz:
      console.log("Trigger-aufrufende Instanz: "+this);
      var material = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors,
        //mit dieser Farbkombi lässt sich ein Unterschied zu den Blau-Farben erkennen ....
  //     ambient: 0xa9bbd6,
       color: 0x00FF00
      })
      _.each(features, function(feature){
        var obj = createExtrudedObject({
          coordinates: feature.geometry.coordinates,
          properties: _.defaults(feature.properties, {
            roof: {}
          })
        }, city.geo, material);
        buildings.push(obj);
        city.publish('addToScene', obj);
      });
    } else {
      _.each(buildings, function(obj){
        city.publish('removeFromScene', obj);
      });
      buildings = [];
    }
  });

  function go(dummy){
    var uses = ['Suedschenkel', 'Westschenkel'];
    var ensemble_colours = [0x0000FF, 0x7D7D7D];
  //  console.log(colours[1]);

    for (var i = 0; i < uses.length; i++) {
      if (dummy === uses[i]){
    //    console.log(i);
    // colours =  colours[i]
 /*   if (dummy) {
      console.log(dummy+ " kommt in Funktion an")
    };
    */
      //aufrufende Instanz:
  //    console.log("Funktion-aufrufende Instanz: "+this);
  //     var material = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors})
      var colorobj = new THREE.MeshLambertMaterial({vertexColors: THREE.VertexColors,
       ambient: 0x00FFFF,
       color: ensemble_colours[i],
       opacity: 0.8
      });
       console.log(colorobj.color);
       console.log(colorobj.opacity);
      _.each(features, function(feature){
        var obj = createExtrudedObject({
          coordinates: feature.geometry.coordinates,
          properties: _.defaults(feature.properties, {
            roof: {}
          })
        }, city.geo, colorobj);
  //      console.log(obj);
        buildings.push(obj);
        city.publish('addToScene', obj);
      });

 /*   else {
      _.each(buildings, function(obj){
        city.publish('removeFromScene', obj);
      });
      buildings = [];
    } */
      }
    };
  }

//  $('#new-buildings').change(go(0));

  $.getJSON('buildings.geojson', function(geojson){
    var ensemble;
    _.each(geojson.features, function(feature){
      ensemble = feature.properties.ensemble;

        feature.properties.area = feature.properties.area || geoJSONArea(feature.geometry);
        // city.data.processArea(feature.geometry.coordinates[0]);
        if (!feature.properties.height) {
          if (feature.properties.maxfloors) {
            feature.properties.height = feature.properties.maxfloors * 3.14;
          } else {
            feature.properties.height = 22;
          }
        }
        // console.log(feature.properties);
        features.push(feature);
        if (ensemble){
          go(ensemble);
        }
        else {
          $('#new-buildings').prop('checked', true).change();
        }
//die (nicht sichtbare) Control 'Zeige Gebäude' wird ANgehakt

    });
  });
});
// }());<|endoftext|>


<commit_before>$( document ).ready(function(){
	$('#signup').submit(function(){
		if ( $('#signup').hasClass('valid')){
			$.ajax({
				url: "https://docs.google.com/forms/d/1XP0D4xLrAWxO0PVhzGtWm61hmjBwn4ljQkXKrVOxs1c/formResponse",
				method: "POST",
				data: {"entry.1190056938": $('.email').val()},
				dataType: "xml"
			}).done(function( data ) {
				});

			//$('input[type="submit"]').val('Thank you! ♥ We\'ll email ' + $('.email').val() + ' when registration opens.');

			document.getElementById('inputName').focus();

			// Uncomment to deploy registration
			$('#inputEmail').val( $('.email').val() );
			PageTransitions.nextPage( $('#submit') );
			document.getElementById('inputName').focus();
			window.scrollTo(0, 0);

			// Comment out when enabling registration
			// $( '#signup' ).addClass('sent');
			// $('input[type="submit"]').val('Thank you! ♥');

		}
		return false;
	});

	$('#registration').submit(function(){
		$( '#signup' ).addClass('sent');

		$.ajax({
			url: "https://docs.google.com/forms/d/1gw8CcNFUIQPnYtsiuIMFNYMpc4zwPCB5dfk4vXKei70/formResponse",
			method: "POST",
			data: {
				"entry.1329019569": $('#inputName').val(),
				"entry.1672038254": $('#inputEmail').val(),
				"entry.129183011": $('#inputPhone').val(),
				"entry.1703554458": $('#inputText').val(),
				"entry.252876870": $('#inputSchool').val(),
				"entry.51384848": $('#inputAge').val(),
				"entry.1195499678": $('#radioFreshman').is(':checked'),
				"entry.1265397744": $('#radioSophomore').is(':checked'),
				"entry.1569566681": $('#radioJunior').is(':checked'),
				"entry.483327531": $('#radioSenior').is(':checked'),
				"entry.984089881": $('#radioMale').is(':checked'),
				"entry.1986352261": $('#radioFemale').is(':checked'),
				"entry.1588954907": $('#radioOther').is(':checked'),
				"entry.2125284338": $('#genderOther').val(),
				"entry.2089511535": $('#radioExtraSmall').is(':checked'),
				"entry.1824583845": $('#radioSmall').is(':checked'),
				"entry.1470106745": $('#radioMedium').is(':checked'),
				"entry.41123227": $('#radioLarge').is(':checked'),
				"entry.1532256680": $('#radioExtraLarge').is(':checked'),
				"entry.1048428711": $('#radioNone').is(':checked'),
				"entry.1370720311": $('#radioVegetarian').is(':checked'),
				"entry.102106713": $('#radioVegan').is(':checked'),
				"entry.2122108574": $('#radioDietOther').is(':checked'),
				"entry.696923945": $('#dietOther').val(),
				"entry.551301075": $('#inputFirst').val(),
				"entry.286314095": $('#inputHardware').val(),
				"entry.1067756311": $('#inputResume').val(),
				"entry.855370543": $('#inputWebsite').val(),
				"entry.1235285041": $('#inputGitHub').val(),
				"entry.1957040337": $('#inputLinkedIn').val(),
				"entry.1561818636": $('#inputFriend1').val(),
				"entry.1546163091": $('#inputFriend2').val(),
				"entry.2097407123": $('#inputFriend3').val()
			},
			dataType: "xml"
		}).done(function( data ) {
			});

		$('input[type="submit"]').val('Thank you! ♥');
		PageTransitions.nextPage( $('#submit') );
		window.scrollTo(0, 0);
		return false;
	});

	$('#cancel').click(function(){
		PageTransitions.nextPage( $('#submit') );
		window.scrollTo(0, 0);
		return false;
	});
});

$('.email').keyup(function(){
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
	re.test(this.value) ? $( '#signup' ).addClass( 'valid' ) : $( '#signup' ).removeClass( 'valid' );
});

// Disabled texting phone validation
// $('#inputText').change(function(){
// 	var regex = /^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$/;
// 	if ( !regex.test($('#inputPhone').val()) && !$('#inputText').is(':checked') ) {
// 		$( '#inputText' ).attr('checked', false);
// 	}
// });<commit_msg>Cleaned up application form<commit_after>$( document ).ready(function(){
	$('#signup').submit(function(){
		if ( $('#signup').hasClass('valid')){
			$.ajax({
				url: "https://docs.google.com/forms/d/1XP0D4xLrAWxO0PVhzGtWm61hmjBwn4ljQkXKrVOxs1c/formResponse",
				method: "POST",
				data: {"entry.1190056938": $('.email').val()},
				dataType: "xml"
			}).done(function( data ) {
				});

			//$('input[type="submit"]').val('Thank you! ♥ We\'ll email ' + $('.email').val() + ' when registration opens.');

			document.getElementById('inputName').focus();

			// Uncomment to deploy registration
			$('#inputEmail').val( $('.email').val() );
			PageTransitions.nextPage( $('#submit') );
			document.getElementById('inputName').focus();
			window.scrollTo(0, 0);

			// Comment out when enabling registration
			// $( '#signup' ).addClass('sent');
			// $('input[type="submit"]').val('Thank you! ♥');

		}
		return false;
	});

	$('#registration').submit(function(){
		$( '#signup' ).addClass('sent');

		var grade = "";
		if ($('#radioFreshman').is(':checked')) grade = "9";
		else if ($('#radioSophomore').is(':checked')) grade = "10";
		else if ($('#radioJunior').is(':checked')) grade = "11";
		else if ($('#radioSenior').is(':checked')) grade = "12";

		var gender = "";
		if ($('#radioMale').is(':checked')) gender = "Male";
		else if ($('#radioFemale').is(':checked')) gender = "Female";
		else if ($('#radioOther').is(':checked')) gender = $('#genderOther').val();

		var shirt = "";
		if ($('#radioExtraSmall').is(':checked')) shirt = "XS";
		else if ($('#radioSmall').is(':checked')) shirt = "S";
		else if ($('#radioMedium').is(':checked')) shirt = "M";
		else if ($('#radioLarge').is(':checked')) shirt = "L";
		else if ($('#radioExtraLarge').is(':checked')) shirt = "XL";

		var diet = "";
		if ($('#radioNone').is(':checked')) diet = "None";
		else if ($('#radioVegetarian').is(':checked')) diet = "Vegetarian";
		else if ($('#radioVegan').is(':checked')) diet = "Vegan";
		else if ($('#radioDietOther').is(':checked')) diet = $('#dietOther').val();

		$.ajax({
			url: "https://docs.google.com/forms/d/1gw8CcNFUIQPnYtsiuIMFNYMpc4zwPCB5dfk4vXKei70/formResponse",
			method: "POST",
			data: {
				"entry.1329019569": $('#inputName').val(),
				"entry.1672038254": $('#inputEmail').val(),
				"entry.129183011": $('#inputPhone').val(),
				"entry.1703554458": $('#inputText').val(),
				"entry.252876870": $('#inputSchool').val(),
				"entry.51384848": $('#inputAge').val(),
				"entry.1195499678": grade,
				"entry.984089881": gender,
				"entry.2089511535": shirt,
				"entry.1048428711": diet,
				"entry.551301075": $('#inputFirst').val(),
				"entry.286314095": $('#inputHardware').val(),
				"entry.1067756311": $('#inputResume').val(),
				"entry.855370543": $('#inputWebsite').val(),
				"entry.1235285041": $('#inputGitHub').val(),
				"entry.1957040337": $('#inputLinkedIn').val(),
				"entry.1561818636": $('#inputFriend1').val(),
				"entry.1546163091": $('#inputFriend2').val(),
				"entry.2097407123": $('#inputFriend3').val()
			},
			dataType: "xml"
		}).done(function( data ) {
			});

		$('input[type="submit"]').val('Thank you! ♥');
		PageTransitions.nextPage( $('#submit') );
		window.scrollTo(0, 0);
		return false;
	});

	$('#cancel').click(function(){
		PageTransitions.nextPage( $('#submit') );
		window.scrollTo(0, 0);
		return false;
	});
});

$('.email').keyup(function(){
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
	re.test(this.value) ? $( '#signup' ).addClass( 'valid' ) : $( '#signup' ).removeClass( 'valid' );
});

// Disabled texting phone validation
// $('#inputText').change(function(){
// 	var regex = /^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$/;
// 	if ( !regex.test($('#inputPhone').val()) && !$('#inputText').is(':checked') ) {
// 		$( '#inputText' ).attr('checked', false);
// 	}
// });<|endoftext|>


<commit_before>var game = {
  colorblind: JSON.parse(localStorage.colorblind) || false,
  language: window.location.hash.substring(1) || 'en',
  level: parseInt(localStorage.level, 10) || 0,
  answers: (localStorage.answers && JSON.parse(localStorage.answers)) || {},
  solved: (localStorage.solved && JSON.parse(localStorage.solved)) || [],
  user: localStorage.user || '',
  changed: false,

  start: function() {
      // navigator.language can include '-'
      // ref: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language
      var requestLang = window.navigator.language.split('-')[0];
      if (window.location.hash === '' && requestLang !== 'en' && messages.languageActive.hasOwnProperty(requestLang)) {
          game.language = requestLang;
          window.location.hash = requestLang;
      }
    game.translate();

    $('#level-counter .total').text(levels.length);
    $('#editor').show();
    $('#share').hide();

    if (!localStorage.user) {
      game.user = '' + (new Date()).getTime() + Math.random().toString(36).slice(1);
      localStorage.setItem('user', game.user);
    }

    this.setHandlers();
    this.loadMenu();
    game.loadLevel(levels[game.level]);
  },

  setHandlers: function() {
    $('#next').on('click', function() {
      $('#code').focus();

      if ($(this).hasClass('disabled')) {
        if (!$('.frog').hasClass('animated')) {
          game.tryagain();
        }

        return;
      }

      $('.frog').addClass('animated bounceOutUp');
      $('.arrow, #next').addClass('disabled');

      setTimeout(function() {
        if (game.level >= levels.length - 1) {
          game.win();
        } else {
          game.next();
        }
      }, 2000);
    });

    $('#code').on('keydown', function(e) {
      if (e.keyCode === 13) {

        if (e.ctrlKey || e.metaKey) {
          e.preventDefault();
          game.check();
          $('#next').click();
          return;
        }

        var max = $(this).data('lines');
        var code = $(this).val();
        var trim = code.trim();
        var codeLength = code.split('\n').length;
        var trimLength = trim.split('\n').length;

        if (codeLength >= max) {

          if (codeLength === trimLength) {
            e.preventDefault();
            $('#next').click();
          } else {
            $('#code').focus().val('').val(trim);
          }
        }
      }
    }).on('input', game.debounce(game.check, 500))
    .on('input', function() {
      game.changed = true;
    });

    $('#editor').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() {
      $(this).removeClass();
    });

    $('#labelReset').on('click', function() {
      var warningReset = messages.warningReset[game.language] || messages.warningReset.en;
      var r = confirm(warningReset);

      if (r) {
        game.level = 0;
        game.answers = {};
        game.solved = [];
        game.loadLevel(levels[0]);

        $('.level-marker').removeClass('solved');
      }
    });

    $('#language').on('click', function() {
      $('#language .tooltip').toggle();
    }).on('click', 'a', function() {
      var language = $(this).text();
      $('#language .toggle').text(language);
    });

    $(window).on('beforeunload', function() {
      game.saveAnswer();
      localStorage.setItem('level', game.level);
      localStorage.setItem('answers', JSON.stringify(game.answers));
      localStorage.setItem('solved', JSON.stringify(game.solved));
    }).on('hashchange', function() {
      game.language = window.location.hash.substring(1) || 'en';
      game.translate();

      $('#tweet iframe').remove();
      var html = 'Tweet ' +
                 'Follow @thomashpark';
      $('#tweet').html(html);
      twttr.widgets.load();

      if (game.language === 'en') {
        history.replaceState({}, document.title, './');
      }
    });

    $("#colorblind-toggle").on("click", function() {
      localStorage.setItem("colorblind", !game.colorblind);
      window.location.reload();
    })
  },

  prev: function() {
    this.level--;

    var levelData = levels[this.level];
    this.loadLevel(levelData);
  },

  next: function() {
    this.level++;

    var levelData = levels[this.level];
    this.loadLevel(levelData);
  },

  loadMenu: function() {
    levels.forEach(function(level, i) {
      var levelMarker = $('').addClass('level-marker').attr('data-level', i).text(i+1);

      if ($.inArray(level.name, game.solved) !== -1) {
        levelMarker.addClass('solved');
      }

      levelMarker.appendTo('#levels');
    });

    $('.level-marker').on('click', function() {
      game.saveAnswer();

      var level = $(this).attr('data-level');
      game.level = parseInt(level, 10);
      game.loadLevel(levels[level]);
    });

    $('#level-indicator').on('click', function() {
      $('#levelsWrapper').toggle();
    });

    $('.arrow.left').on('click', function() {
      if ($(this).hasClass('disabled')) {
        return;
      }

      game.saveAnswer();
      game.prev();
    });

    $('.arrow.right').on('click', function() {
      if ($(this).hasClass('disabled')) {
        return;
      }

      game.saveAnswer();
      game.next();
    });
  },

  loadLevel: function(level) {
    $('#editor').show();
    $('#share').hide();
    $('#background, #pond').removeClass('wrap').attr('style', '').empty();
    $('#levelsWrapper').hide();
    $('.level-marker').removeClass('current').eq(this.level).addClass('current');
    $('#level-counter .current').text(this.level + 1);
    $('#before').text(level.before);
    $('#after').text(level.after);
    $('#next').addClass('disabled');

    var instructions = level.instructions[game.language] || level.instructions.en;
    $('#instructions').html(instructions);

    $('.arrow.disabled').removeClass('disabled');

    if (this.level === 0) {
      $('.arrow.left').addClass('disabled');
    }

    if (this.level === levels.length - 1) {
      $('.arrow.right').addClass('disabled');
    }

    var answer = game.answers[level.name];
    $('#code').val(answer).focus();

    this.loadDocs();

    var lines = Object.keys(level.style).length;
    $('#code').height(20 * lines).data("lines", lines);

    var string = level.board;
    var markup = '';
    var colors = {
      'g': 'green',
      'r': 'red',
      'y': 'yellow'
    };

    for (var i = 0; i < string.length; i++) {
      var c = string.charAt(i);
      var color = colors[c];

      var lilypad = $('
').addClass('lilypad ' + color + (this.colorblind ? ' cb-friendly' : '')).data('color', color);
      var frog = $('
').addClass('frog ' + color + (this.colorblind ? ' cb-friendly' : '')).data('color', color);

      $('
').addClass('bg').css(game.transform()).appendTo(lilypad);
      $('
').addClass('bg animated pulse infinite').appendTo(frog);

      $('#background').append(lilypad);
      $('#pond').append(frog);
    }

    var classes = level.classes;

    if (classes) {
      for (var rule in classes) {
        $(rule).addClass(classes[rule]);
      }
    }

    var selector = level.selector || '';
    $('#background ' + selector).css(level.style);

    game.changed = false;
    game.applyStyles();
    game.check();
  },

  loadDocs: function() {
    $('#instructions code').each(function() {
      var code = $(this);
      var text = code.text();

      if (text in docs) {
        code.addClass('help');
        code.on('mouseenter', function(e) {
          if ($('#instructions .tooltip').length === 0) {
            var html = docs[text][game.language] || docs[text].en;
            var tooltipX = code.offset().left;
            var tooltipY = code.offset().top + code.height() + 13;
            $('

').html(html).css({top: tooltipY, left: tooltipX}).appendTo($('#instructions'));
          }
        }).on('mouseleave', function() {
          $('#instructions .tooltip').remove();
        });
      }
    });
  },

  applyStyles: function() {
    var level = levels[game.level];
    var code = $('#code').val();
    var selector = level.selector || '';
    $('#pond ' +  selector).attr('style', code);
    game.saveAnswer();
  },

  check: function() {
    game.applyStyles();

    var level = levels[game.level];
    var lilypads = {};
    var frogs = {};
    var correct = true;

    $('.frog').each(function() {
      var position = $(this).position();
      position.top = Math.floor(position.top);
      position.left = Math.floor(position.left);

      var key = JSON.stringify(position);
      var val = $(this).data('color');
      frogs[key] = val;
    });

    $('.lilypad').each(function() {
      var position = $(this).position();
      position.top = Math.floor(position.top);
      position.left = Math.floor(position.left);

      var key = JSON.stringify(position);
      var val = $(this).data('color');

      if (!(key in frogs) || frogs[key] !== val) {
        correct = false;
      }
    });

    if (correct) {
      ga('send', {
        hitType: 'event',
        eventCategory: level.name,
        eventAction: 'correct',
        eventLabel: $('#code').val()
      });

      if ($.inArray(level.name, game.solved) === -1) {
        game.solved.push(level.name);
      }

      $('[data-level=' + game.level + ']').addClass('solved');
      $('#next').removeClass('disabled');
    } else {
      ga('send', {
        hitType: 'event',
        eventCategory: level.name,
        eventAction: 'incorrect',
        eventLabel: $('#code').val()
      });

      $('#next').addClass('disabled');
    }
  },

  saveAnswer: function() {
    var level = levels[this.level];
    game.answers[level.name] = $('#code').val();
  },

  tryagain: function() {
    $('#editor').addClass('animated shake');
  },

  win: function() {
    var solution = $('#code').val();

    this.loadLevel(levelWin);

    $('#editor').hide();
    $('#code').val(solution);
    $('#share').show();
    $('.frog .bg').removeClass('pulse').addClass('bounce');
  },

  transform: function() {
    var scale = 1 + ((Math.random() / 5) - 0.2);
    var rotate = 360 * Math.random();

    return {'transform': 'scale(' + scale + ') rotate(' + rotate + 'deg)'};
  },

  translate: function() {
    document.title = messages.title[game.language] || messages.title.en;
    $('html').attr('lang', game.language);

    var level = levels[game.level];
    var instructions = level.instructions[game.language] || level.instructions.en;
    $('#instructions').html(instructions);
    game.loadDocs();

    $('.translate').each(function() {
      var label = $(this).attr('id');
      var text = messages[label][game.language] || messages[label].en;

      $('#' + label).text(text);
    });
  },

  debounce: function(func, wait, immediate) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      var callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
      if (callNow) func.apply(context, args);
    };
  }
};

$(document).ready(function() {
  game.start();
});
<commit_msg>fix undefined localStorage bug<commit_after>var game = {
  colorblind: (localStorage && JSON.parse(localStorage.colorblind)) || false,
  language: window.location.hash.substring(1) || 'en',
  level: parseInt(localStorage.level, 10) || 0,
  answers: (localStorage.answers && JSON.parse(localStorage.answers)) || {},
  solved: (localStorage.solved && JSON.parse(localStorage.solved)) || [],
  user: localStorage.user || '',
  changed: false,

  start: function() {
      // navigator.language can include '-'
      // ref: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language
      var requestLang = window.navigator.language.split('-')[0];
      if (window.location.hash === '' && requestLang !== 'en' && messages.languageActive.hasOwnProperty(requestLang)) {
          game.language = requestLang;
          window.location.hash = requestLang;
      }
    game.translate();

    $('#level-counter .total').text(levels.length);
    $('#editor').show();
    $('#share').hide();

    if (!localStorage.user) {
      game.user = '' + (new Date()).getTime() + Math.random().toString(36).slice(1);
      localStorage.setItem('user', game.user);
    }

    this.setHandlers();
    this.loadMenu();
    game.loadLevel(levels[game.level]);
  },

  setHandlers: function() {
    $('#next').on('click', function() {
      $('#code').focus();

      if ($(this).hasClass('disabled')) {
        if (!$('.frog').hasClass('animated')) {
          game.tryagain();
        }

        return;
      }

      $('.frog').addClass('animated bounceOutUp');
      $('.arrow, #next').addClass('disabled');

      setTimeout(function() {
        if (game.level >= levels.length - 1) {
          game.win();
        } else {
          game.next();
        }
      }, 2000);
    });

    $('#code').on('keydown', function(e) {
      if (e.keyCode === 13) {

        if (e.ctrlKey || e.metaKey) {
          e.preventDefault();
          game.check();
          $('#next').click();
          return;
        }

        var max = $(this).data('lines');
        var code = $(this).val();
        var trim = code.trim();
        var codeLength = code.split('\n').length;
        var trimLength = trim.split('\n').length;

        if (codeLength >= max) {

          if (codeLength === trimLength) {
            e.preventDefault();
            $('#next').click();
          } else {
            $('#code').focus().val('').val(trim);
          }
        }
      }
    }).on('input', game.debounce(game.check, 500))
    .on('input', function() {
      game.changed = true;
    });

    $('#editor').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() {
      $(this).removeClass();
    });

    $('#labelReset').on('click', function() {
      var warningReset = messages.warningReset[game.language] || messages.warningReset.en;
      var r = confirm(warningReset);

      if (r) {
        game.level = 0;
        game.answers = {};
        game.solved = [];
        game.loadLevel(levels[0]);

        $('.level-marker').removeClass('solved');
      }
    });

    $('#language').on('click', function() {
      $('#language .tooltip').toggle();
    }).on('click', 'a', function() {
      var language = $(this).text();
      $('#language .toggle').text(language);
    });

    $(window).on('beforeunload', function() {
      game.saveAnswer();
      localStorage.setItem('level', game.level);
      localStorage.setItem('answers', JSON.stringify(game.answers));
      localStorage.setItem('solved', JSON.stringify(game.solved));
    }).on('hashchange', function() {
      game.language = window.location.hash.substring(1) || 'en';
      game.translate();

      $('#tweet iframe').remove();
      var html = 'Tweet ' +
                 'Follow @thomashpark';
      $('#tweet').html(html);
      twttr.widgets.load();

      if (game.language === 'en') {
        history.replaceState({}, document.title, './');
      }
    });

    $("#colorblind-toggle").on("click", function() {
      localStorage.setItem("colorblind", !game.colorblind);
      window.location.reload();
    })
  },

  prev: function() {
    this.level--;

    var levelData = levels[this.level];
    this.loadLevel(levelData);
  },

  next: function() {
    this.level++;

    var levelData = levels[this.level];
    this.loadLevel(levelData);
  },

  loadMenu: function() {
    levels.forEach(function(level, i) {
      var levelMarker = $('').addClass('level-marker').attr('data-level', i).text(i+1);

      if ($.inArray(level.name, game.solved) !== -1) {
        levelMarker.addClass('solved');
      }

      levelMarker.appendTo('#levels');
    });

    $('.level-marker').on('click', function() {
      game.saveAnswer();

      var level = $(this).attr('data-level');
      game.level = parseInt(level, 10);
      game.loadLevel(levels[level]);
    });

    $('#level-indicator').on('click', function() {
      $('#levelsWrapper').toggle();
    });

    $('.arrow.left').on('click', function() {
      if ($(this).hasClass('disabled')) {
        return;
      }

      game.saveAnswer();
      game.prev();
    });

    $('.arrow.right').on('click', function() {
      if ($(this).hasClass('disabled')) {
        return;
      }

      game.saveAnswer();
      game.next();
    });
  },

  loadLevel: function(level) {
    $('#editor').show();
    $('#share').hide();
    $('#background, #pond').removeClass('wrap').attr('style', '').empty();
    $('#levelsWrapper').hide();
    $('.level-marker').removeClass('current').eq(this.level).addClass('current');
    $('#level-counter .current').text(this.level + 1);
    $('#before').text(level.before);
    $('#after').text(level.after);
    $('#next').addClass('disabled');

    var instructions = level.instructions[game.language] || level.instructions.en;
    $('#instructions').html(instructions);

    $('.arrow.disabled').removeClass('disabled');

    if (this.level === 0) {
      $('.arrow.left').addClass('disabled');
    }

    if (this.level === levels.length - 1) {
      $('.arrow.right').addClass('disabled');
    }

    var answer = game.answers[level.name];
    $('#code').val(answer).focus();

    this.loadDocs();

    var lines = Object.keys(level.style).length;
    $('#code').height(20 * lines).data("lines", lines);

    var string = level.board;
    var markup = '';
    var colors = {
      'g': 'green',
      'r': 'red',
      'y': 'yellow'
    };

    for (var i = 0; i < string.length; i++) {
      var c = string.charAt(i);
      var color = colors[c];

      var lilypad = $('
').addClass('lilypad ' + color + (this.colorblind ? ' cb-friendly' : '')).data('color', color);
      var frog = $('
').addClass('frog ' + color + (this.colorblind ? ' cb-friendly' : '')).data('color', color);

      $('
').addClass('bg').css(game.transform()).appendTo(lilypad);
      $('
').addClass('bg animated pulse infinite').appendTo(frog);

      $('#background').append(lilypad);
      $('#pond').append(frog);
    }

    var classes = level.classes;

    if (classes) {
      for (var rule in classes) {
        $(rule).addClass(classes[rule]);
      }
    }

    var selector = level.selector || '';
    $('#background ' + selector).css(level.style);

    game.changed = false;
    game.applyStyles();
    game.check();
  },

  loadDocs: function() {
    $('#instructions code').each(function() {
      var code = $(this);
      var text = code.text();

      if (text in docs) {
        code.addClass('help');
        code.on('mouseenter', function(e) {
          if ($('#instructions .tooltip').length === 0) {
            var html = docs[text][game.language] || docs[text].en;
            var tooltipX = code.offset().left;
            var tooltipY = code.offset().top + code.height() + 13;
            $('

').html(html).css({top: tooltipY, left: tooltipX}).appendTo($('#instructions'));
          }
        }).on('mouseleave', function() {
          $('#instructions .tooltip').remove();
        });
      }
    });
  },

  applyStyles: function() {
    var level = levels[game.level];
    var code = $('#code').val();
    var selector = level.selector || '';
    $('#pond ' +  selector).attr('style', code);
    game.saveAnswer();
  },

  check: function() {
    game.applyStyles();

    var level = levels[game.level];
    var lilypads = {};
    var frogs = {};
    var correct = true;

    $('.frog').each(function() {
      var position = $(this).position();
      position.top = Math.floor(position.top);
      position.left = Math.floor(position.left);

      var key = JSON.stringify(position);
      var val = $(this).data('color');
      frogs[key] = val;
    });

    $('.lilypad').each(function() {
      var position = $(this).position();
      position.top = Math.floor(position.top);
      position.left = Math.floor(position.left);

      var key = JSON.stringify(position);
      var val = $(this).data('color');

      if (!(key in frogs) || frogs[key] !== val) {
        correct = false;
      }
    });

    if (correct) {
      ga('send', {
        hitType: 'event',
        eventCategory: level.name,
        eventAction: 'correct',
        eventLabel: $('#code').val()
      });

      if ($.inArray(level.name, game.solved) === -1) {
        game.solved.push(level.name);
      }

      $('[data-level=' + game.level + ']').addClass('solved');
      $('#next').removeClass('disabled');
    } else {
      ga('send', {
        hitType: 'event',
        eventCategory: level.name,
        eventAction: 'incorrect',
        eventLabel: $('#code').val()
      });

      $('#next').addClass('disabled');
    }
  },

  saveAnswer: function() {
    var level = levels[this.level];
    game.answers[level.name] = $('#code').val();
  },

  tryagain: function() {
    $('#editor').addClass('animated shake');
  },

  win: function() {
    var solution = $('#code').val();

    this.loadLevel(levelWin);

    $('#editor').hide();
    $('#code').val(solution);
    $('#share').show();
    $('.frog .bg').removeClass('pulse').addClass('bounce');
  },

  transform: function() {
    var scale = 1 + ((Math.random() / 5) - 0.2);
    var rotate = 360 * Math.random();

    return {'transform': 'scale(' + scale + ') rotate(' + rotate + 'deg)'};
  },

  translate: function() {
    document.title = messages.title[game.language] || messages.title.en;
    $('html').attr('lang', game.language);

    var level = levels[game.level];
    var instructions = level.instructions[game.language] || level.instructions.en;
    $('#instructions').html(instructions);
    game.loadDocs();

    $('.translate').each(function() {
      var label = $(this).attr('id');
      var text = messages[label][game.language] || messages[label].en;

      $('#' + label).text(text);
    });
  },

  debounce: function(func, wait, immediate) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      var callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
      if (callNow) func.apply(context, args);
    };
  }
};

$(document).ready(function() {
  game.start();
});
<|endoftext|>


<commit_before>(function() {

'use strict';

var DEBUG = true;

var GAME_WIDTH  = 1920;
var GAME_HEIGHT = 1080;

var GRAVITY = 10000;

var MAX_CHARACTER_VELOCITY = 50;

window.WebFontConfig = {
  google: {
    families: [
      'Lora',
    ],
  },
};

var displayState = function displayState(game, stateLabel) {
  var stateDisplay = game.add.text(
    0, 0,
    'state: ' + stateLabel,
    {
      font:     'Lora',
      fontSize: 36,

      fill:   '#fff',
      stroke: '#000',

      strokeThickness: 3,
    }
  );
};

var constrainVelocity = function constrainVelocity(sprite, maxVelocity) {
  var vx = sprite.body.data.velocity[0];
  var vy = sprite.body.data.velocity[1];

  if (vx * vx + vy * vy > maxVelocity * maxVelocity) {
    var angle = Math.atan2(vy, vx);

    vx = Math.cos(angle) * maxVelocity;
    vy = Math.sin(angle) * maxVelocity;

    sprite.body.data.velocity[0] = vx;
    sprite.body.data.velocity[1] = vy;
  }
};

var loadState = {
  preload: function preload() {
    this.load.script(
      'webfont', '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'
    );
  },

  create: function create() {
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;

    this.scale.pageAlignHorizontally = true;
    this.scale.pageAlignVertically   = true;

    if (DEBUG) {
      displayState(this.game, 'loading');
    }

    this.load.image('null',       'assets/null.png');
    this.load.image('background', 'assets/background.png');
    this.load.image('moon',       'assets/moon.png');

    this.load.image('male',   'assets/characters/male.png');
    this.load.image('female', 'assets/characters/female.png');

    this.load.json('room-data',  'assets/data/rooms.json');
    this.load.json('trait-data', 'assets/data/traits.json');

    this.load.json('character-data',  'assets/data/characters/index.json');
    this.load.json('cook-data',       'assets/data/characters/cook.json');
    this.load.json('gardener-data',   'assets/data/characters/gardener.json');
    this.load.json('lady-data',       'assets/data/characters/lady.json');
    this.load.json('lord-data',       'assets/data/characters/lord.json');
    this.load.json('maid-data',       'assets/data/characters/maid.json');
    this.load.json('mother-data',     'assets/data/characters/mother.json');
    this.load.json('stable-boy-data', 'assets/data/characters/stable-boy.json');

    this.load.physics('physics-data', 'assets/data/physics.json');

    this.load.onLoadComplete.add(function() {
      this.state.start('main');
    }, this);

    this.load.start();
  },
};

var mainState = {
  create: function create() {
    this.setupPhysics();
    this.setupScene();
    this.setupRooms();
    this.setupInput();

    this.addCharacters();

    if (DEBUG) {
      displayState(this.game, 'main');
    }
  },

  update: function update() {
    this.constrainCharacters();

    this.updateRooms();
  },

  setupPhysics: function setupPhysics() {
    this.physics.startSystem(Phaser.Physics.P2JS);

    this.floorCollisionGroup     = this.physics.p2.createCollisionGroup();
    this.characterCollisionGroup = this.physics.p2.createCollisionGroup();

    this.physics.p2.updateBoundsCollisionGroup();

    this.physics.p2.gravity.y = GRAVITY;
  },

  setupScene: function setupScene() {
    this.add.image(0, 0, 'background');

    this.add.image(80, 100, 'moon');

    var floor = this.add.sprite(0, 0, 'null');

    this.physics.p2.enable(floor, DEBUG);

    floor.body.clearShapes();
    floor.body.loadPolygon('physics-data', 'floor');

    floor.body.setCollisionGroup(this.floorCollisionGroup);
    floor.body.collides(this.characterCollisionGroup);

    floor.body.static = true;
  },

  setupRooms: function setupRooms() {
    this.rooms = {};

    var roomData = this.cache.getJSON('room-data');

    for (var roomName in roomData) {
      var data = roomData[roomName];

      var shape = new Phaser.Polygon(data.bounds);

      if (DEBUG) {
        var debugShape = this.add.graphics();

        debugShape.lineStyle(1, 0x000000);
        debugShape.beginFill(0x000000, 0.5);
        debugShape.drawPolygon(shape);
        debugShape.endFill();
      }

      this.rooms[roomName] = {
        shape: shape,
      };
    }
  },

  setupInput: function setupInput() {
    this.pointerBody = new p2.Body();

    this.physics.p2.world.addBody(this.pointerBody);

    this.input.onDown.add(this.onPointerDown, this);
    this.input.onUp.add(this.onPointerUp, this);
    this.input.addMoveCallback(this.onPointerMove, this);
  },

  onPointerDown: function onPointerDown(pointer) {
    // FIMXE: Only do this for the alive children...
    var bodies = this.physics.p2.hitTest(
      pointer.position,
      this.characters.children
    );

    if (!bodies.length) {
      return;
    }

    var physicsPos = [
      this.physics.p2.pxmi(pointer.position.x),
      this.physics.p2.pxmi(pointer.position.y),
    ];

    var localPointInBody = [ 0, 0 ];

    this.touchedCharacterBody = bodies[0].parent;

    this.touchedCharacterBody.toLocalFrame(localPointInBody, physicsPos);

    this.pointerConstraint = this.physics.p2.createLockConstraint(
      this.pointerBody,
      this.touchedCharacterBody,
      [
        this.physics.p2.mpxi(localPointInBody[0]),
        this.physics.p2.mpxi(localPointInBody[1]),
      ]
    );

    this.touchedCharacterBody.removeCollisionGroup(this.floorCollisionGroup);
  },

  onPointerUp: function onPointerUp() {
    if (this.pointerConstraint) {
      this.physics.p2.removeConstraint(this.pointerConstraint);

      delete this.pointerConstraint;
    }

    if (this.touchedCharacterBody) {
      this.touchedCharacterBody.collides(this.floorCollisionGroup);

      delete this.touchedCharacterBody;
    }
  },

  onPointerMove: function onPointerMove(pointer) {
    this.pointerBody.position[0] = this.physics.p2.pxmi(pointer.position.x);
    this.pointerBody.position[1] = this.physics.p2.pxmi(pointer.position.y);
  },

  addCharacters: function addCharacters() {
    this.characters = this.add.group();

    var characterData = this.cache.getJSON('character-data');

    for (var i = 0; i < characterData.all.length; i++) {
      var data = this.cache.getJSON(characterData.all[i] + '-data');

      var room = this.rooms[data.rooms.home];

      var roomPoints = room.shape.toNumberArray();

      var x = {
        min: this.world.width,
        max: 0,
      };

      var y = {
        min: this.world.height,
        max: 0,
      };

      for (var j = 0; j < roomPoints.length; j++) {
        var coord = roomPoints[j];

        if (j % 2) {
          y.min = Math.min(y.min, coord);
          y.max = Math.max(y.max, coord);
        } else {
          x.min = Math.min(x.min, coord);
          x.max = Math.max(x.max, coord);
        }
      }

      this.addCharacter(
        this.rnd.integerInRange(x.min, x.max),
        (y.min + y.max) / 2,
        this.rnd.pick(data.assets)
      );
    }
  },

  addCharacter: function addCharacter(x, y, assetName) {
    var character = this.characters.create(x, y, assetName);

    this.physics.p2.enable(character, DEBUG);

    character.body.fixedRotation = true;

    character.body.setCollisionGroup(this.characterCollisionGroup);
    character.body.collides(this.floorCollisionGroup);

    return character;
  },

  constrainCharacters: function constrainCharacters() {
    this.characters.forEachAlive(function constrain(character) {
      constrainVelocity(character, MAX_CHARACTER_VELOCITY);
    }, this);
  },

  updateRooms: function updateRooms() {
    this.characters.forEachAlive(this.updateRoom, this);
  },

  updateRoom: function updateRoom(character) {
    if (character.position.equals(character.previousPosition)) {
      return;
    }

    for (var roomName in this.rooms) {
      var room = this.rooms[roomName];

      if (room.shape.contains(character.position.x, character.position.y)) {
        character.room = roomName;
      }
    }
  },
};

window.startGame = function startGame() {
  var game = new Phaser.Game(
    GAME_WIDTH, GAME_HEIGHT,
    Phaser.AUTO
  );

  game.state.add('load', loadState);
  game.state.add('main', mainState);

  game.state.start('load');
};

})();
<commit_msg>Check for each exists instead of alive<commit_after>(function() {

'use strict';

var DEBUG = true;

var GAME_WIDTH  = 1920;
var GAME_HEIGHT = 1080;

var GRAVITY = 10000;

var MAX_CHARACTER_VELOCITY = 50;

window.WebFontConfig = {
  google: {
    families: [
      'Lora',
    ],
  },
};

var displayState = function displayState(game, stateLabel) {
  var stateDisplay = game.add.text(
    0, 0,
    'state: ' + stateLabel,
    {
      font:     'Lora',
      fontSize: 36,

      fill:   '#fff',
      stroke: '#000',

      strokeThickness: 3,
    }
  );
};

var constrainVelocity = function constrainVelocity(sprite, maxVelocity) {
  var vx = sprite.body.data.velocity[0];
  var vy = sprite.body.data.velocity[1];

  if (vx * vx + vy * vy > maxVelocity * maxVelocity) {
    var angle = Math.atan2(vy, vx);

    vx = Math.cos(angle) * maxVelocity;
    vy = Math.sin(angle) * maxVelocity;

    sprite.body.data.velocity[0] = vx;
    sprite.body.data.velocity[1] = vy;
  }
};

var loadState = {
  preload: function preload() {
    this.load.script(
      'webfont', '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'
    );
  },

  create: function create() {
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;

    this.scale.pageAlignHorizontally = true;
    this.scale.pageAlignVertically   = true;

    if (DEBUG) {
      displayState(this.game, 'loading');
    }

    this.load.image('null',       'assets/null.png');
    this.load.image('background', 'assets/background.png');
    this.load.image('moon',       'assets/moon.png');

    this.load.image('male',   'assets/characters/male.png');
    this.load.image('female', 'assets/characters/female.png');

    this.load.json('room-data',  'assets/data/rooms.json');
    this.load.json('trait-data', 'assets/data/traits.json');

    this.load.json('character-data',  'assets/data/characters/index.json');
    this.load.json('cook-data',       'assets/data/characters/cook.json');
    this.load.json('gardener-data',   'assets/data/characters/gardener.json');
    this.load.json('lady-data',       'assets/data/characters/lady.json');
    this.load.json('lord-data',       'assets/data/characters/lord.json');
    this.load.json('maid-data',       'assets/data/characters/maid.json');
    this.load.json('mother-data',     'assets/data/characters/mother.json');
    this.load.json('stable-boy-data', 'assets/data/characters/stable-boy.json');

    this.load.physics('physics-data', 'assets/data/physics.json');

    this.load.onLoadComplete.add(function() {
      this.state.start('main');
    }, this);

    this.load.start();
  },
};

var mainState = {
  create: function create() {
    this.setupPhysics();
    this.setupScene();
    this.setupRooms();
    this.setupInput();

    this.addCharacters();

    if (DEBUG) {
      displayState(this.game, 'main');
    }
  },

  update: function update() {
    this.constrainCharacters();

    this.updateRooms();
  },

  setupPhysics: function setupPhysics() {
    this.physics.startSystem(Phaser.Physics.P2JS);

    this.floorCollisionGroup     = this.physics.p2.createCollisionGroup();
    this.characterCollisionGroup = this.physics.p2.createCollisionGroup();

    this.physics.p2.updateBoundsCollisionGroup();

    this.physics.p2.gravity.y = GRAVITY;
  },

  setupScene: function setupScene() {
    this.add.image(0, 0, 'background');

    this.add.image(80, 100, 'moon');

    var floor = this.add.sprite(0, 0, 'null');

    this.physics.p2.enable(floor, DEBUG);

    floor.body.clearShapes();
    floor.body.loadPolygon('physics-data', 'floor');

    floor.body.setCollisionGroup(this.floorCollisionGroup);
    floor.body.collides(this.characterCollisionGroup);

    floor.body.static = true;
  },

  setupRooms: function setupRooms() {
    this.rooms = {};

    var roomData = this.cache.getJSON('room-data');

    for (var roomName in roomData) {
      var data = roomData[roomName];

      var shape = new Phaser.Polygon(data.bounds);

      if (DEBUG) {
        var debugShape = this.add.graphics();

        debugShape.lineStyle(1, 0x000000);
        debugShape.beginFill(0x000000, 0.5);
        debugShape.drawPolygon(shape);
        debugShape.endFill();
      }

      this.rooms[roomName] = {
        shape: shape,
      };
    }
  },

  setupInput: function setupInput() {
    this.pointerBody = new p2.Body();

    this.physics.p2.world.addBody(this.pointerBody);

    this.input.onDown.add(this.onPointerDown, this);
    this.input.onUp.add(this.onPointerUp, this);
    this.input.addMoveCallback(this.onPointerMove, this);
  },

  onPointerDown: function onPointerDown(pointer) {
    var bodies = this.physics.p2.hitTest(
      pointer.position,
      this.characters.children
    );

    if (!bodies.length) {
      return;
    }

    var physicsPos = [
      this.physics.p2.pxmi(pointer.position.x),
      this.physics.p2.pxmi(pointer.position.y),
    ];

    var localPointInBody = [ 0, 0 ];

    this.touchedCharacterBody = bodies[0].parent;

    this.touchedCharacterBody.toLocalFrame(localPointInBody, physicsPos);

    this.pointerConstraint = this.physics.p2.createLockConstraint(
      this.pointerBody,
      this.touchedCharacterBody,
      [
        this.physics.p2.mpxi(localPointInBody[0]),
        this.physics.p2.mpxi(localPointInBody[1]),
      ]
    );

    this.touchedCharacterBody.removeCollisionGroup(this.floorCollisionGroup);
  },

  onPointerUp: function onPointerUp() {
    if (this.pointerConstraint) {
      this.physics.p2.removeConstraint(this.pointerConstraint);

      delete this.pointerConstraint;
    }

    if (this.touchedCharacterBody) {
      this.touchedCharacterBody.collides(this.floorCollisionGroup);

      delete this.touchedCharacterBody;
    }
  },

  onPointerMove: function onPointerMove(pointer) {
    this.pointerBody.position[0] = this.physics.p2.pxmi(pointer.position.x);
    this.pointerBody.position[1] = this.physics.p2.pxmi(pointer.position.y);
  },

  addCharacters: function addCharacters() {
    this.characters = this.add.group();

    var characterData = this.cache.getJSON('character-data');

    for (var i = 0; i < characterData.all.length; i++) {
      var data = this.cache.getJSON(characterData.all[i] + '-data');

      var room = this.rooms[data.rooms.home];

      var roomPoints = room.shape.toNumberArray();

      var x = {
        min: this.world.width,
        max: 0,
      };

      var y = {
        min: this.world.height,
        max: 0,
      };

      for (var j = 0; j < roomPoints.length; j++) {
        var coord = roomPoints[j];

        if (j % 2) {
          y.min = Math.min(y.min, coord);
          y.max = Math.max(y.max, coord);
        } else {
          x.min = Math.min(x.min, coord);
          x.max = Math.max(x.max, coord);
        }
      }

      this.addCharacter(
        this.rnd.integerInRange(x.min, x.max),
        (y.min + y.max) / 2,
        this.rnd.pick(data.assets)
      );
    }
  },

  addCharacter: function addCharacter(x, y, assetName) {
    var character = this.characters.create(x, y, assetName);

    this.physics.p2.enable(character, DEBUG);

    character.body.fixedRotation = true;

    character.body.setCollisionGroup(this.characterCollisionGroup);
    character.body.collides(this.floorCollisionGroup);

    return character;
  },

  constrainCharacters: function constrainCharacters() {
    this.characters.forEachExists(function constrainCharacter(character) {
      constrainVelocity(character, MAX_CHARACTER_VELOCITY);
    }, this);
  },

  updateRooms: function updateRooms() {
    this.characters.forEachExists(this.updateRoom, this);
  },

  updateRoom: function updateRoom(character) {
    if (character.position.equals(character.previousPosition)) {
      return;
    }

    for (var roomName in this.rooms) {
      var room = this.rooms[roomName];

      if (room.shape.contains(character.position.x, character.position.y)) {
        character.room = roomName;
      }
    }
  },
};

window.startGame = function startGame() {
  var game = new Phaser.Game(
    GAME_WIDTH, GAME_HEIGHT,
    Phaser.AUTO
  );

  game.state.add('load', loadState);
  game.state.add('main', mainState);

  game.state.start('load');
};

})();
<|endoftext|>