<commit_before>const express = require('express');
const expressLogging = require('express-logging');
const logger = require('logops');
const bodyParser = require('body-parser');
logger.setLevel('DEBUG');
const fs = require('fs');
const redis = require('redis');
const REDIS_PORT = process.env.REDIS_PORT || 6379;

const app = express();
const redisClient = redis.createClient(REDIS_PORT);

redisClient.on("error", function (err) {
    console.log("Redis error " + err);
});

app.use(expressLogging(logger));
app.use(express.static('public'));
app.use(bodyParser.json());

function saveData(type, req, res) {
    redisClient.zadd('ptu', Date.now(), JSON.stringify(req.body), function(err, reply) {
        if (err) {
            logger.error(err);
            res.status(500).send(err);
        } else {
            res.sendStatus(200);
        }
    });
}

function sendLatestData(type, req, res) {
    // add 'since' parameter
    if (req.query.since) {
        console.log('since ' + req.query.since);
        redisClient.zrangebyscore(type, req.query.since, '+inf', 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var ptus = [];
                for (var i = 0; i < reply.length/2; i++) {
                    var ptu = JSON.parse(reply[i*2]);
                    ptu.Timestamp = parseInt(reply[i*2+1]);
                    ptus.push(ptu)
                }
                res.status(200).send(ptus);
            }
        });
    } else {
        console.log('latest');
        redisClient.zrevrange(type, 0, 1, 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var ptu = JSON.parse(reply[0]);
                ptu.Timestamp = parseInt(reply[1]);
                res.status(200).send(ptu);
            }
        });
    }
}

app.post('/api/ptu', function(req, res) {
    if (req.body.Type != 'PTU') {
        res.sendStatus(400);
        return;
    }

    saveData('ptu', req, res);
});

app.post('/api/wind', function(req, res) {
    if (req.body.Type != 'Wind') {
        res.sendStatus(400);
        return;
    }

    saveData('wind', req, res);
});

app.post('/api/rain', function(req, res) {
    if (req.body.Type != 'Rain') {
        res.sendStatus(400);
        return;
    }

    saveData('rain', req, res);
});

app.get('/api/ptu', function(req, res) {
    sendLatestData('ptu', req, res);
});

app.get('/api/wind', function(req, res) {
    sendLatestData('wind', req, res);
});

app.get('/api/rain', function(req, res) {
    sendLatestData('rain', req, res);
});

app.get('/api/weather', function(req, res) {
    req.memcache.get('openweathermap', function(error, result) {
        if (error) {
            logger.error('error reading memcached: ' + error + ' result: ' + result);
            return res.status(500).end();
        }

        var weatherdata = JSON.parse(result);
        var data = {
            temperature: weatherdata.main.temp,
            humidity: weatherdata.main.humidity,
            pressure: weatherdata.main.pressure,
            windspeed: weatherdata.wind.speed,
            winddir: weatherdata.wind.deg,
            clouds: weatherdata.clouds.all
        };
        res.json(data);
    });
});

var server = app.listen(9001, function() {
    var host = server.address().address;
    var port = server.address().port;

    logger.info('koma-weather-server listening at http://%s:%s', host, port);
}).on('error', function(err) {
    logger.error(err);
});
<commit_msg>simplify apis, fix ascom /api/weather endpoint<commit_after>const express = require('express');
const expressLogging = require('express-logging');
const logger = require('logops');
const bodyParser = require('body-parser');
logger.setLevel('DEBUG');
const fs = require('fs');
const redis = require('redis');
const REDIS_PORT = process.env.REDIS_PORT || 6379;

const app = express();
const redisClient = redis.createClient(REDIS_PORT);

redisClient.on("error", function (err) {
    console.log("Redis error " + err);
});

app.use(expressLogging(logger));
app.use(express.static('public'));
app.use(bodyParser.json());

function saveData(type, req, res) {
    redisClient.zadd(type, Date.now(), JSON.stringify(req.body), function(err, reply) {
        if (err) {
            logger.error(err);
            res.status(500).send(err);
        } else {
            res.sendStatus(200);
        }
    });
}

function sendLatestData(type, req, res) {
    if (req.query.since) {
        redisClient.zrangebyscore(type, req.query.since, '+inf', 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var ptus = [];
                for (var i = 0; i < reply.length/2; i++) {
                    var ptu = JSON.parse(reply[i*2]);
                    ptu.Timestamp = parseInt(reply[i*2+1]);
                    ptus.push(ptu)
                }
                res.status(200).send(ptus);
            }
        });
    } else {
        redisClient.zrevrange(type, 0, 1, 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var ptu = JSON.parse(reply[0]);
                ptu.Timestamp = parseInt(reply[1]);
                res.status(200).send(ptu);
            }
        });
    }
}

function dewpoint(humidity, temperature) {
    var a, b;
    if (temperature >= 0) {
        a = 7.5;
        b = 237.3;
    } else {
        a = 7.6;
        b = 240.7;
    }

    var sdd = 6.1078 * Math.pow(10, (a * temperature) / (b + temperature));
    var dd = sdd * (humidity / 100);
    v = Math.log(dd / 6.1078) / Math.log(10);
    return (b * v) / (a - v);
}

app.post('/api', function(req, res) {
    if (req.body.Type == 'PTU') {
        saveData('ptu', req, res);
    } else if (req.body.Type == 'Wind') {
        saveData('wind', req, res);
    } else if (req.body.Type == 'Rain') {
        saveData('rain', req, res);
    } else if (req.body.Type == 'Status') {
        saveData('status', req, res);
    } else {
        res.sendStatus(400);
        return;
    }
});

app.get('/api/ptu', function(req, res) {
    sendLatestData('ptu', req, res);
});

app.get('/api/wind', function(req, res) {
    sendLatestData('wind', req, res);
});

app.get('/api/rain', function(req, res) {
    sendLatestData('rain', req, res);
});

app.get('/api/status', function(req, res) {
    sendLatestData('status', req, res);
});

app.get('/api/weather', function(req, res) {
    redisClient.zrevrange('ptu', 0, 0, function(err, ptustr) {
        if (err) {
            res.status(500).send(err);
            return;
        }
        redisClient.zrevrange('wind', 0, 0, function(err, windstr) {
            if (err) {
                res.status(500).send(err);
                return;
            }
            redisClient.zrevrange('rain', 0, 0, function(err, rainstr) {
                if (err) {
                    res.status(500).send(err);
                    return;
                }
                var ptu = JSON.parse(ptustr[0]);
                var wind = JSON.parse(windstr[0]);
                var rain = JSON.parse(rainstr[0]);

                var data = {
                    temperature: ptu.Data.Temperature.Ambient[0],
                    humidity: ptu.Data.Humidity[0],
                    dewpoint: parseInt(dewpoint(ptu.Data.Humidity[0], ptu.Data.Temperature.Ambient[0])*100)/100,
                    pressure: ptu.Data.Pressure[0],
                    windspeed: wind.Data.Speed.average[0],
                    windgust: wind.Data.Speed.limits[1][0],
                    winddir: wind.Data.Direction.average[0],
                    rainrate: rain.Data.Rain.Intensity[0],
                    cloudcover: 0,
                    skytemperature: 0,
                    skyquality: 0
                };
                res.json(data);
            });
        });
    });
});

var server = app.listen(9001, function() {
    var host = server.address().address;
    var port = server.address().port;

    logger.info('koma-weather-server listening at http://%s:%s', host, port);
}).on('error', function(err) {
    logger.error(err);
});
<|endoftext|>


<commit_before>
/*** = Description
  ** Implements the client-server interface.
  **
  ** COMM.Transporter manages the client side of the server-client-server
  ** data synchronization and the server-client command channel.
  **
  ** It uses COMM.Session for session key handling, COMM.Queue for command
  ** queuing and COMM.Values for data value management.
  **
  ** COMM.Transporter operates in a fully automatic mode and starts when
  ** the document has been loaded.
  **
  ** Don't call any of its methods from your code.
***/
//var//RSence.COMM
COMM.Transporter = HApplication.extend({

/** Sets up the default settings upon construction.
  **/
  constructor: function(){
    var _this = this;
    _this._detectNativeJSONSupport();
    _this.serverLostMessage = 'Server Connection Lost: Reconnecting...';
    _this.label = 'Transporter';
    _this.url = false;
    _this.busy = false;
    _this.stop = true;
    _this._serverInterruptView = false;
    _this._clientEvalError = false;
    _this._busyFlushTimeout = false;
    _this.base(1);
  },

  _detectNativeJSONSupport: function(){
    if(window.JSON){
      var
      _JSON = window.JSON,
      _fun = 'function';
      if((typeof _JSON.parse === _fun) && (typeof _JSON.stringify === _fun)){
        this.parseResponseArray = this._nativeParseResponseArray;
      }
    }
  },

/** Tries to (re)connect to the server as often as possible,
  * mandated essentially by the priority of its
  * HApplication instance.
  **/
  onIdle: function(){
    this.sync();
  },

/** (Re)sets the priority of itself, effects how
  * frequently +onIdle+ is called.
  * Usually set by the server.
  **/
  poll: function(_pri){
    HSystem.reniceApp(this.appId,_pri);
  },

/** Returns the last transaction error of itself. Used by +sync+
  * to report js errors to the server.
  * If no error, returns an empty string.
  **/
  // getClientEvalError: function(){
  //   var _this = COMM.Transporter;
  //   return _this._clientEvalError?'&err_msg=' +
  //          COMM.Values._encodeString(_this._clientEvalError):'';
  // },

  parseResponseArray: function( _responseText ){
    return this.decodeObject( _responseText );
  },

  _nativeParseResponseArray: function( _responseText ){
    return JSON.parse( _responseText );
  },

  setValues: function( _values ){
    if(!_values instanceof Object){
      console.log("Invalid values block: ", _values );
      return;
    }
    var
    i = 0,
    _valueManager = COMM.Values,
    _itemtype,
    _valueId,
    _valueData;
    if(_values['new'] instanceof Array){
      for(i=0;i<_values['new'].length;i++){
        _valueId = _values['new'][i][0];
        _valueData = _values['new'][i][1];
        _valueManager.create( _valueId, _valueData );
      }
    }
    if(_values.set instanceof Array){
      for(i=0;i<_values.set.length;i++){
        _valueId = _values.set[i][0];
        _valueData = _values.set[i][1];
        _valueManager.s( _valueId, _valueData );
      }
    }
    if(_values.del instanceof Array){
      for(i=0;i<_values.del.length;i++){
        _valueId = _values.del[i];
        _valueManager.del( _valueId );
      }
    }
  },

/** = Description
  * Handles synchronization responses.
  *
  * Upon a successful request, this method is called by
  * the onSuccess event of the XMLHttpRequest.
  *
  * It splits up the response string and passes the response
  * messages to COMM.Queue for execution.
  *
  * Parameters:
  * +resp+:: The response object.
  *
  **/
  success: function(resp){
    var _this = COMM.Transporter;
    if(!resp.X.responseText){
      _this.failure(resp);
      return;
    }
    var
    _responseArray = _this.parseResponseArray(resp.X.responseText),
    i = 2,
    _responseArrayLen = _responseArray.length,
    _sesKey = _responseArray[0],
    _values = _responseArray[1],
    _session = COMM.Session,
    _queue = COMM.Queue,
    _errorText;
    if(_sesKey === ''){
      console.log('Invalid session, error message should follow...');
    }
    else {
      _session.newKey(_sesKey);
    }
    _this.setValues( _values );
    _outputScript = '(function(_queue){';
    for(;i<_responseArrayLen;i++){
      _outputScript += '_queue.push( (function(){';
      _outputScript += _responseArray[i];
      _outputScript += '}) );';
      // try {
      //   console.log(_responseArray[i]);
      //   _queue.pushEval( _responseArray[i] );
      // }
      // catch(e) {
      //   console.log('clientEvalError:',_queue.clientException( e, _responseArray[i] ));
      //   _this._clientEvalError = _queue.clientException( e, _responseArray[i] );
      // }
    }
    _outputScript += '_queue.push( (function(){COMM.Transporter.flushBusy();}) );';
    _outputScript += '_queue.push( (function(){_queue.delScript("'+_sesKey+'");}) );';
    _outputScript += '_queue.flush();';
    _outputScript += '})(COMM.Queue);';
    _queue.addScript(_sesKey,_outputScript);
    if(_this._serverInterruptView && _sesKey !== '' ){
      _this._serverInterruptView.die();
      _this._serverInterruptView = false;
    }
  },

/** Sets the +busy+ flag to false and resynchronizes immediately,
  * if COMM.Values contain any unsynchronized values.
  **/
  flushBusy: function(){
    var _this = COMM.Transporter;
    _this.busy = false;
    if( COMM.Values.tosync.length !== 0 ){
      _this.sync();
    }
  },
  failMessage: function(_title,_message){
    var _this = COMM.Transporter,
        _queue = COMM.Queue;
    console.log('failMessage title:',_title,', message:',_message);
    _this.stop = true;
    _queue.push(function(){jsLoader.load('default_theme');});
    _queue.push(function(){jsLoader.load('controls');});
    _queue.push(function(){jsLoader.load('servermessage');});
    _queue.push(function(){ReloadApp.nu(_title,_message);});
  },

  setInterruptAnim: function(_customMessage,_customColor){
    var _this = COMM.Transporter;
    if(!_this._serverInterruptView){
      _this._serverInterruptView = HView.extend({
        _setCustomMessage: function(_text){
          ELEM.setHTML(this._messageDiv,_text);
        },
        _setCustomColor: function(_color){
          this.setStyle('background-color',_color);
        },
        _setFailedResp: function(_resp){
          if( _resp !== undefined && typeof _resp !== 'string' ){
            this._failedResp = _resp;
          }
          this._errorIndex++;
          return this;
        },
        _retry: function(){
          this._retryIndex++;
          var _resp = this._failedResp;
          COMM.request(
            _resp.url,
            _resp.options
          );
        },
        onIdle: function(){
          var _currentDate = new Date().getTime();
          this.bringToFront();
          if( this._errorIndex > 0 &&
              (this._retryIndex !== this._errorIndex) &&
              (this._lastError + 2000 < _currentDate) &&
              this._failedResp ){
            this._lastError = _currentDate;
            this._retry();
          }
          this.base();
        },
        _errorIndex: 0,
        _retryIndex: 0,
        _lastError: new Date().getTime(),
        die: function(){
          var _app = this.app;
          HSystem.reniceApp(_app.appId,this._origPriority);
          this.base();
          _app.sync();
        },
        drawSubviews: function(){
          var _style = [
            ['padding-left', '8px'],
            ['background-color', '#600'],
            ['text-align','center'],
            ['color', '#fff'],
            ['font-size', '16px'],
            ['opacity', 0.85]
          ], i = 0;
          for( ; i<_style.length; i++ ){
            this.setStyle( _style[i][0], _style[i][1] );
          }
          this._messageDiv = ELEM.make(this.elemId);
          ELEM.setHTML(this._messageDiv,this.app.serverLostMessage);
          this._origPriority = HSystem.appPriorities[this.appId];
          if(HSystem.appPriorities[this.appId]<10){
            HSystem.reniceApp(this.appId,10);
          }
          this._anim = HView.extend({
            _animIndex: 0,
            _anim: function(){
              var _targetRect,
                  _width = ELEM.getSize(this.parent.elemId)[0];
              this._animIndex++;
              if(this._animIndex%2===0){
                _targetRect = HRect.nu(0,0,80,20);
              }
              else {
                _targetRect = HRect.nu(_width-80,0,_width,20);
              }
              this.animateTo(_targetRect,2000);
            },
            onAnimationEnd: function(){
              if(this.drawn){
                this._anim();
              }
            }
          }
        ).nu( [0,0,80,20], this ).setStyle('background-color','#fff').setStyle('opacity',0.8)._anim();
        }
      }).nu([0,0,200,20,0,null],_this);
      if( typeof _customMessage !== 'string' ){
        _this._serverInterruptView._setFailedResp(_customMessage);
      }
    }
    if( _customMessage !== undefined ) {
      if( typeof _customMessage === 'string' ){
        _this._serverInterruptView._setCustomMessage(_customMessage);
      }
    }
    if( !_this.stop ){
      _this._serverInterruptView._setFailedResp();
    }
    if( _customColor !== undefined ){
      _this._serverInterruptView._setCustomColor(_customColor);
    }
  },

/** Called by the XMLHttpRequest, when there was a failure in communication.
  **/
  failure: function(_resp){
    var _this = COMM.Transporter;
    // server didn't respond, likely network issue.. retry.
    if(_resp.X.status===0){
      console.log(_this.serverLostMessage);
      _this.setInterruptAnim(_resp);
    }
    else {
      _this.failMessage('Transporter Error','Transporter was unable to complete the synchronization request.');
    }
  },

/** Starts requests.
  **/
  sync: function(){
    if(this.stop){
      // console.log('sync stop');
      return;
    }
    if(this.busy){
      // console.log('sync busy');
      return;
    }
    // console.log('sync.');
    this.busy = true;
    var _now = new Date().getTime();
    if(window.sesWatcher && window.sesWatcher.sesTimeoutValue){
      // Sets the value of the session watcher to the current time. It could cause an unnecessary re-sync poll immediately after this sync otherwise.
      sesWatcher.sesTimeoutValue.set( _now );
    }
    var
    _this = this,
    // _values = HVM.sync(),
    // _boundary = _now.toString(36)+(Math.random()*10000).toString(36)+(Math.random()*10000).toString(36),
    // _separator = '--'+_boundary,
    // _errorMessage = _this.getClientEvalError(),
    _body = COMM.Values.sync();
    // _body = _separator+
    //   '\r\nContent-Disposition: form-data; name="ses_key"\r\nContent-Type: text/plain\r\n'+
    //   '\r\n'+COMM.Session.ses_key+'\r\n'+_separator;
    // if( _values ){
    //   _body += '\r\nContent-Disposition: form-data; name="values"\r\nContent-Type: application/json; charset=UTF-8\r\n'+
    //   '\r\n'+_values+'\r\n'+_separator+'--\r\n';
    // }
    // else {
    //   _body += '--\r\n';
    // }
    // _body = [_sesKey,_errorMessage,_values?'&values='+_values:''].join('');
    COMM.request(
      _this.url, {
        _this: _this,
        // contentType: 'multipart/form-data; boundary='+_boundary,
        contentType: 'application/json',
        onSuccess: COMM.Transporter.success,
        onFailure: COMM.Transporter.failure,
        method: 'POST',
        async: true,
        body: _body
      }
    );
  }
}).nu();
<commit_msg>Support for value type in the protocol; for pushvalue and pullvalue<commit_after>
/*** = Description
  ** Implements the client-server interface.
  **
  ** COMM.Transporter manages the client side of the server-client-server
  ** data synchronization and the server-client command channel.
  **
  ** It uses COMM.Session for session key handling, COMM.Queue for command
  ** queuing and COMM.Values for data value management.
  **
  ** COMM.Transporter operates in a fully automatic mode and starts when
  ** the document has been loaded.
  **
  ** Don't call any of its methods from your code.
***/
//var//RSence.COMM
COMM.Transporter = HApplication.extend({

/** Sets up the default settings upon construction.
  **/
  constructor: function(){
    var _this = this;
    _this._detectNativeJSONSupport();
    _this.serverLostMessage = 'Server Connection Lost: Reconnecting...';
    _this.label = 'Transporter';
    _this.url = false;
    _this.busy = false;
    _this.stop = true;
    _this._serverInterruptView = false;
    _this._clientEvalError = false;
    _this._busyFlushTimeout = false;
    _this.base(1);
  },

  _detectNativeJSONSupport: function(){
    if(window.JSON){
      var
      _JSON = window.JSON,
      _fun = 'function';
      if((typeof _JSON.parse === _fun) && (typeof _JSON.stringify === _fun)){
        this.parseResponseArray = this._nativeParseResponseArray;
      }
    }
  },

/** Tries to (re)connect to the server as often as possible,
  * mandated essentially by the priority of its
  * HApplication instance.
  **/
  onIdle: function(){
    this.sync();
  },

/** (Re)sets the priority of itself, effects how
  * frequently +onIdle+ is called.
  * Usually set by the server.
  **/
  poll: function(_pri){
    HSystem.reniceApp(this.appId,_pri);
  },

/** Returns the last transaction error of itself. Used by +sync+
  * to report js errors to the server.
  * If no error, returns an empty string.
  **/
  // getClientEvalError: function(){
  //   var _this = COMM.Transporter;
  //   return _this._clientEvalError?'&err_msg=' +
  //          COMM.Values._encodeString(_this._clientEvalError):'';
  // },

  parseResponseArray: function( _responseText ){
    return this.decodeObject( _responseText );
  },

  _nativeParseResponseArray: function( _responseText ){
    return JSON.parse( _responseText );
  },

  setValues: function( _values ){
    if(!_values instanceof Object){
      console.log("Invalid values block: ", _values );
      return;
    }
    var
    i = 0,
    _value,
    _valueType,
    _valueManager = COMM.Values,
    _itemtype,
    _valueId,
    _valueData;
    if(_values['new'] instanceof Array){
      for(i=0;i<_values['new'].length;i++){
        _value = _values['new'][i];
        _valueId = _value[0];
        _valueData = _value[1];
        _valueType = 0;
        if( _value.length === 3 ){
          _valueType = _value[2];
        }
        _valueManager.create( _valueId, _valueData, _valueType );
      }
    }
    if(_values.set instanceof Array){
      for(i=0;i<_values.set.length;i++){
        _value = _values.set[i];
        _valueId = _value[0];
        _valueData = _value[1];
        _valueManager.s( _valueId, _valueData );
      }
    }
    if(_values.del instanceof Array){
      for(i=0;i<_values.del.length;i++){
        _valueId = _values.del[i];
        _valueManager.del( _valueId );
      }
    }
  },

/** = Description
  * Handles synchronization responses.
  *
  * Upon a successful request, this method is called by
  * the onSuccess event of the XMLHttpRequest.
  *
  * It splits up the response string and passes the response
  * messages to COMM.Queue for execution.
  *
  * Parameters:
  * +resp+:: The response object.
  *
  **/
  success: function(resp){
    var _this = COMM.Transporter;
    if(!resp.X.responseText){
      _this.failure(resp);
      return;
    }
    var
    _responseArray = _this.parseResponseArray(resp.X.responseText),
    i = 2,
    _responseArrayLen = _responseArray.length,
    _sesKey = _responseArray[0],
    _values = _responseArray[1],
    _session = COMM.Session,
    _queue = COMM.Queue,
    _errorText;
    if(_sesKey === ''){
      console.log('Invalid session, error message should follow...');
    }
    else {
      _session.newKey(_sesKey);
    }
    _this.setValues( _values );
    _outputScript = '(function(_queue){';
    for(;i<_responseArrayLen;i++){
      _outputScript += '_queue.push( (function(){';
      _outputScript += _responseArray[i];
      _outputScript += '}) );';
      // try {
      //   console.log(_responseArray[i]);
      //   _queue.pushEval( _responseArray[i] );
      // }
      // catch(e) {
      //   console.log('clientEvalError:',_queue.clientException( e, _responseArray[i] ));
      //   _this._clientEvalError = _queue.clientException( e, _responseArray[i] );
      // }
    }
    _outputScript += '_queue.push( (function(){COMM.Transporter.flushBusy();}) );';
    _outputScript += '_queue.push( (function(){_queue.delScript("'+_sesKey+'");}) );';
    _outputScript += '_queue.flush();';
    _outputScript += '})(COMM.Queue);';
    _queue.addScript(_sesKey,_outputScript);
    if(_this._serverInterruptView && _sesKey !== '' ){
      _this._serverInterruptView.die();
      _this._serverInterruptView = false;
    }
  },

/** Sets the +busy+ flag to false and resynchronizes immediately,
  * if COMM.Values contain any unsynchronized values.
  **/
  flushBusy: function(){
    var _this = COMM.Transporter;
    _this.busy = false;
    if( COMM.Values.tosync.length !== 0 ){
      _this.sync();
    }
  },
  failMessage: function(_title,_message){
    var _this = COMM.Transporter,
        _queue = COMM.Queue;
    console.log('failMessage title:',_title,', message:',_message);
    _this.stop = true;
    _queue.push(function(){jsLoader.load('default_theme');});
    _queue.push(function(){jsLoader.load('controls');});
    _queue.push(function(){jsLoader.load('servermessage');});
    _queue.push(function(){ReloadApp.nu(_title,_message);});
  },

  setInterruptAnim: function(_customMessage,_customColor){
    var _this = COMM.Transporter;
    if(!_this._serverInterruptView){
      _this._serverInterruptView = HView.extend({
        _setCustomMessage: function(_text){
          ELEM.setHTML(this._messageDiv,_text);
        },
        _setCustomColor: function(_color){
          this.setStyle('background-color',_color);
        },
        _setFailedResp: function(_resp){
          if( _resp !== undefined && typeof _resp !== 'string' ){
            this._failedResp = _resp;
          }
          this._errorIndex++;
          return this;
        },
        _retry: function(){
          this._retryIndex++;
          var _resp = this._failedResp;
          COMM.request(
            _resp.url,
            _resp.options
          );
        },
        onIdle: function(){
          var _currentDate = new Date().getTime();
          this.bringToFront();
          if( this._errorIndex > 0 &&
              (this._retryIndex !== this._errorIndex) &&
              (this._lastError + 2000 < _currentDate) &&
              this._failedResp ){
            this._lastError = _currentDate;
            this._retry();
          }
          this.base();
        },
        _errorIndex: 0,
        _retryIndex: 0,
        _lastError: new Date().getTime(),
        die: function(){
          var _app = this.app;
          HSystem.reniceApp(_app.appId,this._origPriority);
          this.base();
          _app.sync();
        },
        drawSubviews: function(){
          var _style = [
            ['padding-left', '8px'],
            ['background-color', '#600'],
            ['text-align','center'],
            ['color', '#fff'],
            ['font-size', '16px'],
            ['opacity', 0.85]
          ], i = 0;
          for( ; i<_style.length; i++ ){
            this.setStyle( _style[i][0], _style[i][1] );
          }
          this._messageDiv = ELEM.make(this.elemId);
          ELEM.setHTML(this._messageDiv,this.app.serverLostMessage);
          this._origPriority = HSystem.appPriorities[this.appId];
          if(HSystem.appPriorities[this.appId]<10){
            HSystem.reniceApp(this.appId,10);
          }
          this._anim = HView.extend({
            _animIndex: 0,
            _anim: function(){
              var _targetRect,
                  _width = ELEM.getSize(this.parent.elemId)[0];
              this._animIndex++;
              if(this._animIndex%2===0){
                _targetRect = HRect.nu(0,0,80,20);
              }
              else {
                _targetRect = HRect.nu(_width-80,0,_width,20);
              }
              this.animateTo(_targetRect,2000);
            },
            onAnimationEnd: function(){
              if(this.drawn){
                this._anim();
              }
            }
          }
        ).nu( [0,0,80,20], this ).setStyle('background-color','#fff').setStyle('opacity',0.8)._anim();
        }
      }).nu([0,0,200,20,0,null],_this);
      if( typeof _customMessage !== 'string' ){
        _this._serverInterruptView._setFailedResp(_customMessage);
      }
    }
    if( _customMessage !== undefined ) {
      if( typeof _customMessage === 'string' ){
        _this._serverInterruptView._setCustomMessage(_customMessage);
      }
    }
    if( !_this.stop ){
      _this._serverInterruptView._setFailedResp();
    }
    if( _customColor !== undefined ){
      _this._serverInterruptView._setCustomColor(_customColor);
    }
  },

/** Called by the XMLHttpRequest, when there was a failure in communication.
  **/
  failure: function(_resp){
    var _this = COMM.Transporter;
    // server didn't respond, likely network issue.. retry.
    if(_resp.X.status===0){
      console.log(_this.serverLostMessage);
      _this.setInterruptAnim(_resp);
    }
    else {
      _this.failMessage('Transporter Error','Transporter was unable to complete the synchronization request.');
    }
  },

/** Starts requests.
  **/
  sync: function(){
    if(this.stop){
      // console.log('sync stop');
      return;
    }
    if(this.busy){
      // console.log('sync busy');
      return;
    }
    // console.log('sync.');
    this.busy = true;
    var _now = new Date().getTime();
    if(window.sesWatcher && window.sesWatcher.sesTimeoutValue){
      // Sets the value of the session watcher to the current time. It could cause an unnecessary re-sync poll immediately after this sync otherwise.
      sesWatcher.sesTimeoutValue.set( _now );
    }
    var
    _this = this,
    // _values = HVM.sync(),
    // _boundary = _now.toString(36)+(Math.random()*10000).toString(36)+(Math.random()*10000).toString(36),
    // _separator = '--'+_boundary,
    // _errorMessage = _this.getClientEvalError(),
    _body = COMM.Values.sync();
    // _body = _separator+
    //   '\r\nContent-Disposition: form-data; name="ses_key"\r\nContent-Type: text/plain\r\n'+
    //   '\r\n'+COMM.Session.ses_key+'\r\n'+_separator;
    // if( _values ){
    //   _body += '\r\nContent-Disposition: form-data; name="values"\r\nContent-Type: application/json; charset=UTF-8\r\n'+
    //   '\r\n'+_values+'\r\n'+_separator+'--\r\n';
    // }
    // else {
    //   _body += '--\r\n';
    // }
    // _body = [_sesKey,_errorMessage,_values?'&values='+_values:''].join('');
    COMM.request(
      _this.url, {
        _this: _this,
        // contentType: 'multipart/form-data; boundary='+_boundary,
        contentType: 'application/json',
        onSuccess: COMM.Transporter.success,
        onFailure: COMM.Transporter.failure,
        method: 'POST',
        async: true,
        body: _body
      }
    );
  }
}).nu();
<|endoftext|>


<commit_before>import path from 'path';
import fs from 'fs';
import { Repository } from './data/models';

const repoConfig = (app) => {
  app.post('/api/configs/eslintrc/:id/:action', async (req, res, next) => {
    const id = req.params.id;
    const filePath = path.resolve(__dirname, `../files/configs/eslintrc-${id}.json`);

    if (!req.isAuthenticated()) {
      return res.status(403).send('Permission denied.');
    }

    if (req.params.action === 'get') {
      const repo = await Repository.findById(id);

      if (repo.userId !== req.user.id) {
        return res.status(403).send('Permission denied.');
      }

      if (!repo.hasConfig) {
        return res.status(404).send('Not found.');
      }

      return res.sendFile(filePath);
    }

    try {
      const config = JSON.parse(req.body.config);

      fs.writeFile(filePath, config, async (err) => {
        if (err) throw err;

        if (!req.body.hasConfig) {
          const repo = await Repository.update({ hasConfig: true }, { where: { id: id } });
        }

        return res.json();
      });
    } catch (e) {
      return res.status(500).send('Something when wrong.');
    }
  });
}

export default repoConfig;
<commit_msg>Moving configs out of files dir<commit_after>import path from 'path';
import fs from 'fs';
import { Repository } from './data/models';

const repoConfig = (app) => {
  app.post('/api/configs/eslintrc/:id/:action', async (req, res, next) => {
    const id = req.params.id;
    const filePath = path.resolve(__dirname, `../configs/eslintrc-${id}.json`);

    if (!req.isAuthenticated()) {
      return res.status(403).send('Permission denied.');
    }

    if (req.params.action === 'get') {
      const repo = await Repository.findById(id);

      if (repo.userId !== req.user.id) {
        return res.status(403).send('Permission denied.');
      }

      if (!repo.hasConfig) {
        return res.status(404).send('Not found.');
      }

      return res.sendFile(filePath);
    }

    try {
      const config = JSON.parse(req.body.config);

      fs.writeFile(filePath, config, async (err) => {
        if (err) throw err;

        if (!req.body.hasConfig) {
          const repo = await Repository.update({ hasConfig: true }, { where: { id: id } });
        }

        return res.json();
      });
    } catch (e) {
      return res.status(500).send('Something when wrong.');
    }
  });
}

export default repoConfig;
<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * View-specific Properties and properties that are common to all screens.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const BooleanProperty = require( 'AXON/BooleanProperty' );
  const GQConstants = require( 'GRAPHING_QUADRATICS/common/GQConstants' );
  const GQQueryParameters = require( 'GRAPHING_QUADRATICS/common/GQQueryParameters' );
  const graphingQuadratics = require( 'GRAPHING_QUADRATICS/graphingQuadratics' );
  const Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object} [options]
   */
  class GQViewProperties {

    constructor( options ) {

      options = _.extend( {
        equationForm: 'standard', // see GQConstants.EQUATION_FORMS
        graphContentsVisible: true,
        equationAccordionBoxExpanded: true,
        equationsVisible: GQQueryParameters.checkAll,
        tandem: Tandem.required
      }, options );

      assert && assert( GQConstants.EQUATION_FORMS.includes( options.equationForm ),
        'invalid equationForm: ' + options.equationForm );

      // @public form of equations used to label curves
      this.equationForm = options.equationForm;

      // @public
      this.graphContentsVisibleProperty = new BooleanProperty( options.graphContentsVisible, {
        tandem: options.tandem.createTandem( 'graphContentsVisibleProperty' ),
        phetioDocumentation: 'whether the contents of the graph are visible'
      } );

      // @public
      this.equationAccordionBoxExpandedProperty = new BooleanProperty( options.equationAccordionBoxExpanded, {
        tandem: options.tandem.createTandem( 'equationAccordionBoxExpandedProperty' ),
        phetioDocumentation: 'whether the equation accordion box is expanded'
      } );

      // @public
      this.equationsVisibleProperty = new BooleanProperty( options.equationsVisible, {
        tandem: options.tandem.createTandem( 'equationsVisibleProperty' ),
        phetioDocumentation: 'whether equations are visible on graphed curves'
      } );
    }

    /**
     * @public
     */
    reset() {
      this.graphContentsVisibleProperty.reset();
      this.equationAccordionBoxExpandedProperty.reset();
      this.equationsVisibleProperty.reset();
    }
  }

  return graphingQuadratics.register( 'GQViewProperties', GQViewProperties );
} );

 <commit_msg>doc<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * View-specific Properties and properties that are common to all screens.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const BooleanProperty = require( 'AXON/BooleanProperty' );
  const GQConstants = require( 'GRAPHING_QUADRATICS/common/GQConstants' );
  const GQQueryParameters = require( 'GRAPHING_QUADRATICS/common/GQQueryParameters' );
  const graphingQuadratics = require( 'GRAPHING_QUADRATICS/graphingQuadratics' );
  const Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object} [options]
   */
  class GQViewProperties {

    constructor( options ) {

      options = _.extend( {
        equationForm: 'standard', // see GQConstants.EQUATION_FORMS
        graphContentsVisible: true,
        equationAccordionBoxExpanded: true,
        equationsVisible: GQQueryParameters.checkAll,
        tandem: Tandem.required
      }, options );

      assert && assert( GQConstants.EQUATION_FORMS.includes( options.equationForm ),
        'invalid equationForm: ' + options.equationForm );

      // @public {string} form of equations used to label curves. It is not necessary to expose this via PhET-iO.
      this.equationForm = options.equationForm;

      // @public
      this.graphContentsVisibleProperty = new BooleanProperty( options.graphContentsVisible, {
        tandem: options.tandem.createTandem( 'graphContentsVisibleProperty' ),
        phetioDocumentation: 'whether the contents of the graph are visible'
      } );

      // @public
      this.equationAccordionBoxExpandedProperty = new BooleanProperty( options.equationAccordionBoxExpanded, {
        tandem: options.tandem.createTandem( 'equationAccordionBoxExpandedProperty' ),
        phetioDocumentation: 'whether the equation accordion box is expanded'
      } );

      // @public
      this.equationsVisibleProperty = new BooleanProperty( options.equationsVisible, {
        tandem: options.tandem.createTandem( 'equationsVisibleProperty' ),
        phetioDocumentation: 'whether equations are visible on graphed curves'
      } );
    }

    /**
     * @public
     */
    reset() {
      this.graphContentsVisibleProperty.reset();
      this.equationAccordionBoxExpandedProperty.reset();
      this.equationsVisibleProperty.reset();
    }
  }

  return graphingQuadratics.register( 'GQViewProperties', GQViewProperties );
} );

 <|endoftext|>


<commit_before><commit_msg>fix: add sso redirects<commit_after><|endoftext|>


<commit_before>/**
 * @author AnastasiaVictor/ http://github.com/avmakesthings
 *
 * A-frame Particle system component using
 *
 */

var SPE = require('../scripts/SPE.min.js');

AFRAME.registerComponent('particles', {
	schema: {
		preset: {type:'', default: 'dustCloud'}
    },
    init: function(){

        switch(this.data.preset){
            case 'dustCloud':
                this.createDustCloud()
                break
            case 'fireflies':
                this.createFireflies()
                break
            default:
                console.log('no particle system preset selected')
        }
    },
    tick: function(){

        this.particleGroup.tick( this.timeDelta/2 )
    },
    createDustCloud: function(){

        var texture = new THREE.TextureLoader().load( '../assets/cloud.png' )

        var particleGroup = this.particleGroup = new SPE.Group({
            texture: {
                value: texture
            },
            blending: THREE.NormalBlending,
            fog: true,
            maxParticleCount: 750
        });

        var emitter = new SPE.Emitter({
            particleCount: 300,
            maxAge: {
                value: 30,
            },
            position: {
                value: new THREE.Vector3( 0, -15, -50 ),
                spread: new THREE.Vector3( 100, 30, 100 )
            },
            velocity: {
                value: new THREE.Vector3( 0, 0, 2 )
            },
            wiggle: {
                spread: 10
            },
            size: {
                value: 75,
                spread: 50
            },
            opacity: {
                value: [ 0, 0.2, 0 ]
            },
            color: {
                value: new THREE.Color( 244, 206, 168 ),
                spread: new THREE.Color( 0.1, 0.1, 0.1 )
            },
            angle: {
                value: [ 0, Math.PI * 0.125 ]
            }
        });

        particleGroup.addEmitter( emitter )
        this.el.object3D.add( particleGroup.mesh )

    },
    createFireflies: function(){

        var thisTex = new THREE.TextureLoader().load( '../assets/smokeparticle.png' )

        var particleGroup = this.particleGroup = new SPE.Group({
            texture: {
                value: thisTex
            },
            maxParticleCount: 200
        });
        var emitter = new SPE.Emitter({
            type: SPE.distributions.BOX,
            maxAge: {
                value: 20
            },
            position: {
                value: new THREE.Vector3(0, 10, 0),
                spread: new THREE.Vector3( 100, 100, 100)
            },
            acceleration: {
                value: new THREE.Vector3(0, 0, 0),
                spread: new THREE.Vector3( 0, 0, 0 )
            },
            velocity: {
                value: new THREE.Vector3(1, 0.3, 1),
                spread: new THREE.Vector3(0.5, 1, 0.5)
            },
            color: {
                value: new THREE.Color('white')
            },
            size: {
                value: 1
            },
            particleCount: 100,
            randomize: false,
            opacity: {
                value: [ 1 ]
            },
            direction: 1,
            rotationAxis:'x',
            rotationAngle: 3.14,
            duration: null
        });

        particleGroup.addEmitter( emitter )
        this.el.object3D.add( particleGroup.mesh )

    },
})
<commit_msg>updated header<commit_after>/**
 * @author AnastasiaVictor/ http://github.com/avmakesthings
 *
 * A-frame Particle system component using Shader Particle Engine
 * by @squarefeet https://github.com/squarefeet/ShaderParticleEngine
 */

var SPE = require('../scripts/SPE.min.js');

AFRAME.registerComponent('particles', {
	schema: {
		preset: {type:'', default: 'dustCloud'}
    },
    init: function(){

        switch(this.data.preset){
            case 'dustCloud':
                this.createDustCloud()
                break
            case 'fireflies':
                this.createFireflies()
                break
            default:
                console.log('no particle system preset selected')
        }
    },
    tick: function(){

        this.particleGroup.tick( this.timeDelta/2 )
    },
    createDustCloud: function(){

        var texture = new THREE.TextureLoader().load( '../assets/cloud.png' )

        var particleGroup = this.particleGroup = new SPE.Group({
            texture: {
                value: texture
            },
            blending: THREE.NormalBlending,
            fog: true,
            maxParticleCount: 750
        });

        var emitter = new SPE.Emitter({
            particleCount: 300,
            maxAge: {
                value: 30,
            },
            position: {
                value: new THREE.Vector3( 0, -15, -50 ),
                spread: new THREE.Vector3( 100, 30, 100 )
            },
            velocity: {
                value: new THREE.Vector3( 0, 0, 2 )
            },
            wiggle: {
                spread: 10
            },
            size: {
                value: 75,
                spread: 50
            },
            opacity: {
                value: [ 0, 0.2, 0 ]
            },
            color: {
                value: new THREE.Color( 244, 206, 168 ),
                spread: new THREE.Color( 0.1, 0.1, 0.1 )
            },
            angle: {
                value: [ 0, Math.PI * 0.125 ]
            }
        });

        particleGroup.addEmitter( emitter )
        this.el.object3D.add( particleGroup.mesh )

    },
    createFireflies: function(){

        var thisTex = new THREE.TextureLoader().load( '../assets/smokeparticle.png' )

        var particleGroup = this.particleGroup = new SPE.Group({
            texture: {
                value: thisTex
            },
            maxParticleCount: 200
        });
        var emitter = new SPE.Emitter({
            type: SPE.distributions.BOX,
            maxAge: {
                value: 20
            },
            position: {
                value: new THREE.Vector3(0, 10, 0),
                spread: new THREE.Vector3( 100, 100, 100)
            },
            acceleration: {
                value: new THREE.Vector3(0, 0, 0),
                spread: new THREE.Vector3( 0, 0, 0 )
            },
            velocity: {
                value: new THREE.Vector3(1, 0.3, 1),
                spread: new THREE.Vector3(0.5, 1, 0.5)
            },
            color: {
                value: new THREE.Color('white')
            },
            size: {
                value: 1
            },
            particleCount: 100,
            randomize: false,
            opacity: {
                value: [ 1 ]
            },
            direction: 1,
            rotationAxis:'x',
            rotationAngle: 3.14,
            duration: null
        });

        particleGroup.addEmitter( emitter )
        this.el.object3D.add( particleGroup.mesh )

    },
})
<|endoftext|>


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

	// Your starting point. Enjoy the ride!

})(window);
<commit_msg>Added code comments<commit_after>(function (window) { //this function takes window object which represents a window containing the DOM document.
		     //I believe the function(window) is saying that in this function the code will be part of the DOM document and be displayed in the browser as part of the page.
	'use strict';

	// Your starting point. Enjoy the ride!

})(window);
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import renderer from 'react-test-renderer';
import HomeScreen from './index.js';
import UserGateway, {Permissions} from '../../gateway/user.js';
import PatientDashboard from '../PatientDashboard/';
import AdminDashboard from '../AdminDashboard/';
import HelperDashboard from '../HelperDashboard/';

describe('Home Screen', () => {

    beforeEach(() => {
        UserGateway.signIn('sarah@emailprovider.com');
    });

    it('should use the patient dashboard when the user is the focus', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.FOCUS);

        const homeScreen = renderer
            .create(<HomeScreen />)
            .getInstance();

        expect(homeScreen.state.screen.type).toBe(PatientDashboard);
    });

    it('should use the admin dashboard when the user is an admin', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.ADMIN);

        const homeScreen = renderer
            .create(<HomeScreen />)
            .getInstance();

        expect(homeScreen.state.screen.type).toBe(AdminDashboard);
    });

    it('should use the helper dashboard when the user is a helper', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.HELPER);

        const homeScreen = renderer
            .create(<HomeScreen />)
            .getInstance();

        expect(homeScreen.state.screen.type).toBe(HelperDashboard);
    });

    it('should render the correct screen', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.HELPER);

        const homeScreen = renderer
            .create(<HomeScreen />);

        expect(homeScreen).toMatchSnapshot();
    });
});
<commit_msg>Fixing imports in tests<commit_after>import React, {Component} from 'react';
import renderer from 'react-test-renderer';
import HomeScreen from './index.js';
import UserGateway, {Permissions} from '../../gateway/user.js';
import PatientDashboard from './PatientDashboard/';
import AdminDashboard from './AdminDashboard/';
import HelperDashboard from './HelperDashboard/';

describe('Home Screen', () => {

    beforeEach(() => {
        UserGateway.signIn('sarah@emailprovider.com');
    });

    it('should use the patient dashboard when the user is the focus', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.FOCUS);

        const homeScreen = renderer
            .create(<HomeScreen />)
            .getInstance();

        expect(homeScreen.state.screen.type).toBe(PatientDashboard);
    });

    it('should use the admin dashboard when the user is an admin', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.ADMIN);

        const homeScreen = renderer
            .create(<HomeScreen />)
            .getInstance();

        expect(homeScreen.state.screen.type).toBe(AdminDashboard);
    });

    it('should use the helper dashboard when the user is a helper', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.HELPER);

        const homeScreen = renderer
            .create(<HomeScreen />)
            .getInstance();

        expect(homeScreen.state.screen.type).toBe(HelperDashboard);
    });

    it('should render the correct screen', () => {
        const spy = jest.spyOn(UserGateway, 'getCurrentPermissions')
                        .mockReturnValue(Permissions.HELPER);

        const homeScreen = renderer
            .create(<HomeScreen />);

        expect(homeScreen).toMatchSnapshot();
    });
});
<|endoftext|>


<commit_before>(function (module) {
  var mapController = {};

  mapController.index = function (ctx, next) {

    if (!ctx.params.lat) {
      var latLng = {
        lat: 47.3232,
        lng: -120.3232
      };
      var zoom = 6;
    } else {

      var latLng = {
        lat: parseFloat(ctx.params.lat),
        lng: parseFloat(ctx.params.lng)
      };
      var zoom = 15;
    }

    ctx.latLng = latLng;
    ctx.zoom = zoom;

    $('.loading').show();
    $('#about-container').hide();

      initMap(latLng, zoom);

    next();
  };

  mapController.findSchools = function (ctx, next) {
    $('.loading').show();
    var ref = new Firebase('https://intense-heat-7080.firebaseio.com/');
    ref.child('schools').limitToFirst(2).once('value', function (snapshot) {
      ctx.schools = snapshot.val();
      next();
    }, function (errorObject) {
      console.log('The read failed: ' + errorObject.code);
    });
  };

  mapController.renderSchools = function (ctx, next) {
    keys = Object.keys(ctx.schools);
    schoolArray = [];
    keys.map(function (k) {
      schoolArray.push({
        key: k,
        latLng: {
          lat: ctx.schools[k].lat,
          lng: ctx.schools[k].lng
        },
        school: ctx.schools[k].school_name,
        percentPersonalExemption: Math.ceil(ctx.schools[k].percent_with_personal_exemption * 100),
        percentReligiousExemption: Math.ceil(ctx.schools[k].percent_with_religious_exemption * 100),
        percentMedicalExemption: Math.ceil(ctx.schools[k].percent_with_medical_exemption * 100),
        percentTotalExemption: Math.ceil(ctx.schools[k].percent_with_any_exemption * 100),
        percentCompletedImmunization: Math.ceil(ctx.schools[k].percent_complete_for_all_immunizations * 100),
        totalEnrollment: ctx.schools[k].k_12_enrollment
      });
      $('#home-container').hide();
      $('#map-container').show();
      $('.loading').hide();

    });

    var filteredSchoolArray = schoolArray.filter(function (k) {
      return (-123 < k.latLng.lng) && (-117 > k.latLng.lng) && (45 < k.latLng.lat) && (49 > k.latLng.lat);
    });

    filteredSchoolArray.map(function (school) {
      var marker = new google.maps.Marker({
        position: school.latLng,
        content: '
' + school.school + '

Personal Exemption: ' + school.percentPersonalExemption + '%

Religious Exemption: ' + school.percentReligiousExemption + '%

Medical Exemption: ' + school.percentMedicalExemption + '%

Total Exemption: ' + school.percentTotalExemption + '%

Completed Immunization: ' + school.percentCompletedImmunization + '%

Total Enrollment: ' + school.totalEnrollment + '
',
        key: school.key,
        name1: school.school,
        data1: [school.percentPersonalExemption, school.percentReligiousExemption, school.percentMedicalExemption, school.percentCompletedImmunization]
      });

      marker.addListener('click', function () {
        displayChart(marker.name1, marker.data1);
        $('#chart-wrapper').slideToggle('slow');
        $('#school-data h1').text(marker.name1);
        $('#school-data').html(marker.content);
      });

      var infoWindow = new google.maps.InfoWindow();
      // content: contentString
      infoWindow.setContent(marker.content);

      var someArray = marker.content;
      // To add the marker to the map, call setMap();

      marker.setMap(map);
      google.maps.event.trigger(map, 'resize');
      map.setCenter(ctx.latLng);
      map.setZoom(ctx.zoom);
    });
    next();
  };

  module.mapController = mapController;
})(window);
<commit_msg>removed limit<commit_after>(function (module) {
  var mapController = {};

  mapController.index = function (ctx, next) {

    if (!ctx.params.lat) {
      var latLng = {
        lat: 47.3232,
        lng: -120.3232
      };
      var zoom = 6;
    } else {

      var latLng = {
        lat: parseFloat(ctx.params.lat),
        lng: parseFloat(ctx.params.lng)
      };
      var zoom = 15;
    }

    ctx.latLng = latLng;
    ctx.zoom = zoom;

    $('.loading').show();
    $('#about-container').hide();

      initMap(latLng, zoom);

    next();
  };

  mapController.findSchools = function (ctx, next) {
    $('.loading').show();
    var ref = new Firebase('https://intense-heat-7080.firebaseio.com/');
    ref.child('schools').once('value', function (snapshot) {
      ctx.schools = snapshot.val();
      next();
    }, function (errorObject) {
      console.log('The read failed: ' + errorObject.code);
    });
  };

  mapController.renderSchools = function (ctx, next) {
    keys = Object.keys(ctx.schools);
    schoolArray = [];
    keys.map(function (k) {
      schoolArray.push({
        key: k,
        latLng: {
          lat: ctx.schools[k].lat,
          lng: ctx.schools[k].lng
        },
        school: ctx.schools[k].school_name,
        percentPersonalExemption: Math.ceil(ctx.schools[k].percent_with_personal_exemption * 100),
        percentReligiousExemption: Math.ceil(ctx.schools[k].percent_with_religious_exemption * 100),
        percentMedicalExemption: Math.ceil(ctx.schools[k].percent_with_medical_exemption * 100),
        percentTotalExemption: Math.ceil(ctx.schools[k].percent_with_any_exemption * 100),
        percentCompletedImmunization: Math.ceil(ctx.schools[k].percent_complete_for_all_immunizations * 100),
        totalEnrollment: ctx.schools[k].k_12_enrollment
      });
      $('#home-container').hide();
      $('#map-container').show();
      $('.loading').hide();

    });

    var filteredSchoolArray = schoolArray.filter(function (k) {
      return (-123 < k.latLng.lng) && (-117 > k.latLng.lng) && (45 < k.latLng.lat) && (49 > k.latLng.lat);
    });

    filteredSchoolArray.map(function (school) {
      var marker = new google.maps.Marker({
        position: school.latLng,
        content: '
' + school.school + '

Personal Exemption: ' + school.percentPersonalExemption + '%

Religious Exemption: ' + school.percentReligiousExemption + '%

Medical Exemption: ' + school.percentMedicalExemption + '%

Total Exemption: ' + school.percentTotalExemption + '%

Completed Immunization: ' + school.percentCompletedImmunization + '%

Total Enrollment: ' + school.totalEnrollment + '
',
        key: school.key,
        name1: school.school,
        data1: [school.percentPersonalExemption, school.percentReligiousExemption, school.percentMedicalExemption, school.percentCompletedImmunization]
      });

      marker.addListener('click', function () {
        displayChart(marker.name1, marker.data1);
        $('#chart-wrapper').slideToggle('slow');
        $('#school-data h1').text(marker.name1);
        $('#school-data').html(marker.content);
      });

      var infoWindow = new google.maps.InfoWindow();
      // content: contentString
      infoWindow.setContent(marker.content);

      var someArray = marker.content;
      // To add the marker to the map, call setMap();

      marker.setMap(map);
      google.maps.event.trigger(map, 'resize');
      map.setCenter(ctx.latLng);
      map.setZoom(ctx.zoom);
    });
    next();
  };

  module.mapController = mapController;
})(window);
<|endoftext|>


<commit_before>(function () {
  var ns = $.namespace("pskl.controller");
  ns.DrawingController = function (framesheet, container, dpi) {
    // TODO(vincz): Store user prefs in a localstorage string ?
    var renderingOptions = {
      "dpi": dpi,
      "hasGrid" : true
    };

    /**
     * @public
     */
    this.framesheet = framesheet;

    /**
     * @public
     */
    this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(framesheet.getCurrentFrame());

    /**
     * @private
     */
    this.container = container;

    this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "drawing-canvas");
    this.overlayRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "canvas-overlay");

    this.renderer.init(framesheet.getCurrentFrame());
    this.overlayRenderer.init(this.overlayFrame);

    // State of drawing controller:
    this.isClicked = false;
    this.isRightClicked = false;
    this.previousMousemoveTime = 0;
    this.currentToolBehavior = null;
    this.primaryColor =  Constants.DEFAULT_PEN_COLOR;
    this.secondaryColor =  Constants.TRANSPARENT_COLOR;

    this.initMouseBehavior();

    $.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
      console.log("Tool selected: ", toolBehavior);
      this.currentToolBehavior = toolBehavior;
    }, this));

    $.subscribe(Events.COLOR_SELECTED, $.proxy(function(evt, color, isPrimary) {
      console.log("Color selected: ", color);
      if (isPrimary) {
        this.primaryColor = color;
      } else {
        this.secondaryColor = color;
      }
    }, this));
  };

  ns.DrawingController.prototype.initMouseBehavior = function() {
    var body = $('body');
        this.container.mousedown($.proxy(this.onMousedown_, this));
        this.container.mousemove($.proxy(this.onMousemove_, this));
        body.mouseup($.proxy(this.onMouseup_, this));

        // Deactivate right click:
        this.container.contextmenu(this.onCanvasContextMenu_);
  };

  /**
   * @private
   */
  ns.DrawingController.prototype.onMousedown_ = function (event) {
      this.isClicked = true;

      if(event.button == 2) { // right click
        this.isRightClicked = true;
        $.publish(Events.CANVAS_RIGHT_CLICKED);
      }

      var coords = this.getSpriteCoordinates(event);

      this.currentToolBehavior.applyToolAt(
        coords.col, coords.row,
        this.getCurrentColor_(),
        this.framesheet.getCurrentFrame(),
        this.overlayFrame
      );

      $.publish(Events.LOCALSTORAGE_REQUEST);
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.onMousemove_ = function (event) {
      var currentTime = new Date().getTime();
      // Throttling of the mousemove event:
      if ((currentTime - this.previousMousemoveTime) > 40 ) {
        var coords = this.getSpriteCoordinates(event);
        if (this.isClicked) {

          this.currentToolBehavior.moveToolAt(
            coords.col, coords.row,
            this.getCurrentColor_(),
            this.framesheet.getCurrentFrame(),
            this.overlayFrame
          );

          // TODO(vincz): Find a way to move that to the model instead of being at the interaction level.
          // Eg when drawing, it may make sense to have it here. However for a non drawing tool,
          // you don't need to draw anything when mousemoving and you request useless localStorage.
          $.publish(Events.LOCALSTORAGE_REQUEST);
        } else {

          this.currentToolBehavior.moveUnactiveToolAt(
            coords.col, coords.row,
            this.getCurrentColor_(),
            this.framesheet.getCurrentFrame(),
            this.overlayFrame
          );
        }
        this.previousMousemoveTime = currentTime;
      }
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.onMouseup_ = function (event) {
      if(this.isClicked || this.isRightClicked) {
        // A mouse button was clicked on the drawing canvas before this mouseup event,
        // the user was probably drawing on the canvas.
        // Note: The mousemove movement (and the mouseup) may end up outside
        // of the drawing canvas.

        this.isClicked = false;
        this.isRightClicked = false;

        var coords = this.getSpriteCoordinates(event);
        //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row);
        this.currentToolBehavior.releaseToolAt(
          coords.col, coords.row,
          this.getCurrentColor_(),
          this.framesheet.getCurrentFrame(),
          this.overlayFrame
        );

        $.publish(Events.TOOL_RELEASED);
      }
    },

    /**
   * @private
   */
    ns.DrawingController.prototype.getRelativeCoordinates = function (clientX, clientY) {
      var canvasPageOffset = this.container.offset();
      return {
        x : clientX - canvasPageOffset.left,
        y : clientY - canvasPageOffset.top
      };
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.getSpriteCoordinates = function(event) {
        var coords = this.getRelativeCoordinates(event.clientX, event.clientY);
        return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords);
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.getCurrentColor_ = function () {
      if(this.isRightClicked) {
        return this.secondaryColor;
      } else {
        return this.primaryColor;
      }
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) {
      event.preventDefault();
      event.stopPropagation();
      event.cancelBubble = true;
      return false;
    };

  ns.DrawingController.prototype.updateDPI = function (newDPI) {
    this.renderer.updateDPI(newDPI);
    this.overlayRenderer.updateDPI(newDPI);
    this.forceRendering_();
  };

  ns.DrawingController.prototype.render = function () {
    this.renderFrame();
    this.renderOverlay();
  };

  ns.DrawingController.prototype.renderFrame = function () {
    var frame = this.framesheet.getCurrentFrame();
    var serializedFrame = frame.serialize();
    if (this.serializedFrame != serializedFrame) {
      this.serializedFrame = serializedFrame;
      this.renderer.render(frame);
    }
  };

  ns.DrawingController.prototype.renderOverlay = function () {
    var serializedOverlay = this.overlayFrame.serialize();
    if (this.serializedOverlay != serializedOverlay) {
      this.serializedOverlay = serializedOverlay;
      this.overlayRenderer.render(this.overlayFrame);
    }
  };

  ns.DrawingController.prototype.forceRendering_ = function () {
    this.serializedFrame = this.serializedOverlay = null;
  }
})();<commit_msg>Context menu needs to also be deactivated on the palette, to easily pick a previously used color as a secondary color.<commit_after>(function () {
  var ns = $.namespace("pskl.controller");
  ns.DrawingController = function (framesheet, container, dpi) {
    // TODO(vincz): Store user prefs in a localstorage string ?
    var renderingOptions = {
      "dpi": dpi,
      "hasGrid" : true
    };

    /**
     * @public
     */
    this.framesheet = framesheet;

    /**
     * @public
     */
    this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(framesheet.getCurrentFrame());

    /**
     * @private
     */
    this.container = container;

    this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "drawing-canvas");
    this.overlayRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "canvas-overlay");

    this.renderer.init(framesheet.getCurrentFrame());
    this.overlayRenderer.init(this.overlayFrame);

    // State of drawing controller:
    this.isClicked = false;
    this.isRightClicked = false;
    this.previousMousemoveTime = 0;
    this.currentToolBehavior = null;
    this.primaryColor =  Constants.DEFAULT_PEN_COLOR;
    this.secondaryColor =  Constants.TRANSPARENT_COLOR;

    this.initMouseBehavior();

    $.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
      console.log("Tool selected: ", toolBehavior);
      this.currentToolBehavior = toolBehavior;
    }, this));

    $.subscribe(Events.COLOR_SELECTED, $.proxy(function(evt, color, isPrimary) {
      console.log("Color selected: ", color);
      if (isPrimary) {
        this.primaryColor = color;
      } else {
        this.secondaryColor = color;
      }
    }, this));
  };

  ns.DrawingController.prototype.initMouseBehavior = function() {
    var body = $('body');
        this.container.mousedown($.proxy(this.onMousedown_, this));
        this.container.mousemove($.proxy(this.onMousemove_, this));
        body.mouseup($.proxy(this.onMouseup_, this));

        // Deactivate right click:
        body.contextmenu(this.onCanvasContextMenu_);
  };

  /**
   * @private
   */
  ns.DrawingController.prototype.onMousedown_ = function (event) {
      this.isClicked = true;

      if(event.button == 2) { // right click
        this.isRightClicked = true;
        $.publish(Events.CANVAS_RIGHT_CLICKED);
      }

      var coords = this.getSpriteCoordinates(event);

      this.currentToolBehavior.applyToolAt(
        coords.col, coords.row,
        this.getCurrentColor_(),
        this.framesheet.getCurrentFrame(),
        this.overlayFrame
      );

      $.publish(Events.LOCALSTORAGE_REQUEST);
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.onMousemove_ = function (event) {
      var currentTime = new Date().getTime();
      // Throttling of the mousemove event:
      if ((currentTime - this.previousMousemoveTime) > 40 ) {
        var coords = this.getSpriteCoordinates(event);
        if (this.isClicked) {

          this.currentToolBehavior.moveToolAt(
            coords.col, coords.row,
            this.getCurrentColor_(),
            this.framesheet.getCurrentFrame(),
            this.overlayFrame
          );

          // TODO(vincz): Find a way to move that to the model instead of being at the interaction level.
          // Eg when drawing, it may make sense to have it here. However for a non drawing tool,
          // you don't need to draw anything when mousemoving and you request useless localStorage.
          $.publish(Events.LOCALSTORAGE_REQUEST);
        } else {

          this.currentToolBehavior.moveUnactiveToolAt(
            coords.col, coords.row,
            this.getCurrentColor_(),
            this.framesheet.getCurrentFrame(),
            this.overlayFrame
          );
        }
        this.previousMousemoveTime = currentTime;
      }
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.onMouseup_ = function (event) {
      if(this.isClicked || this.isRightClicked) {
        // A mouse button was clicked on the drawing canvas before this mouseup event,
        // the user was probably drawing on the canvas.
        // Note: The mousemove movement (and the mouseup) may end up outside
        // of the drawing canvas.

        this.isClicked = false;
        this.isRightClicked = false;

        var coords = this.getSpriteCoordinates(event);
        //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row);
        this.currentToolBehavior.releaseToolAt(
          coords.col, coords.row,
          this.getCurrentColor_(),
          this.framesheet.getCurrentFrame(),
          this.overlayFrame
        );

        $.publish(Events.TOOL_RELEASED);
      }
    },

    /**
   * @private
   */
    ns.DrawingController.prototype.getRelativeCoordinates = function (clientX, clientY) {
      var canvasPageOffset = this.container.offset();
      return {
        x : clientX - canvasPageOffset.left,
        y : clientY - canvasPageOffset.top
      };
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.getSpriteCoordinates = function(event) {
        var coords = this.getRelativeCoordinates(event.clientX, event.clientY);
        return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords);
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.getCurrentColor_ = function () {
      if(this.isRightClicked) {
        return this.secondaryColor;
      } else {
        return this.primaryColor;
      }
    };

    /**
   * @private
   */
    ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) {
      event.preventDefault();
      event.stopPropagation();
      event.cancelBubble = true;
      return false;
    };

  ns.DrawingController.prototype.updateDPI = function (newDPI) {
    this.renderer.updateDPI(newDPI);
    this.overlayRenderer.updateDPI(newDPI);
    this.forceRendering_();
  };

  ns.DrawingController.prototype.render = function () {
    this.renderFrame();
    this.renderOverlay();
  };

  ns.DrawingController.prototype.renderFrame = function () {
    var frame = this.framesheet.getCurrentFrame();
    var serializedFrame = frame.serialize();
    if (this.serializedFrame != serializedFrame) {
      this.serializedFrame = serializedFrame;
      this.renderer.render(frame);
    }
  };

  ns.DrawingController.prototype.renderOverlay = function () {
    var serializedOverlay = this.overlayFrame.serialize();
    if (this.serializedOverlay != serializedOverlay) {
      this.serializedOverlay = serializedOverlay;
      this.overlayRenderer.render(this.overlayFrame);
    }
  };

  ns.DrawingController.prototype.forceRendering_ = function () {
    this.serializedFrame = this.serializedOverlay = null;
  }
})();<|endoftext|>


<commit_before>import * as THREE from '../lib/three';

import { queryData } from '../utils/url';
import props from '../props';
import {
  tempVector,
  createInstancedMesh,
  setIdentityMatrix,
  set180RotationMatrix,
} from '../utils/three';
import viewer from '../viewer';
import settings from '../settings';
import {
  getCostumeColor,
  getRoomColor,
  recordCostumeColor,
  highlightColor,
} from '../theme/colors';
import streamJSON from '../lib/stream-json';
import * as roomUtils from './utils';
import layout from './layout';
import dummyTextureUrl from '../public/dummy.png';
import audio from '../audio';
import * as serializer from '../utils/serializer';
import feature from '../utils/feature';

const PROTOCOL = location.protocol;

let roomIndex = 0;
let wallMesh;
let wallMeshes;
let roomMesh;
let floorMesh;
let roomVerticalMesh;
let horizontalVerticalCornerMesh;
let verticalHorizontalCornerMesh;
let roomMeshes;
let floorMeshes;
let roomVerticalMeshes;
let horizontalVerticalCornerMeshes;
let verticalHorizontalCornerMeshes;
let headMesh;
let handMesh;

let meshesByType;

const roomOffset = new THREE.Vector3(0, settings.roomHeight * 0.5, 0);
const roomsGroup = new THREE.Group();
roomsGroup.matrixAutoUpdate = false;

const debugMesh = new THREE.Mesh(
  new THREE.BoxGeometry(0, 0, 0),
  new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load(dummyTextureUrl),
  })
);
debugMesh.frustumCulled = false;

const getFrame = (frames, number) => {
  let frame = frames[number];
  if (!frame) frame--;
  // Check if data is still a string:
  if (frame[0] === '[') {
    frame = frames[number] = JSON.parse(frame);
  }
  return frame;
};

export default class Room {
  constructor({ url, recording, index, pathRecording, single }) {
    this._worldPosition = new THREE.Vector3();
    const placementIndex = this.placementIndex = index === undefined
      ? roomIndex
      : index;
    this.index = roomIndex;
    roomIndex += 1;

    this.isRecording = !!recording;
    if (url) {
       // remove everything after '?':
      this.url = (url.indexOf('?') === -1) ? url : url.split('?')[0];
    }
    this.pathRecording = pathRecording;
    this.fps = 90;
    if (recording) {
      this.hideHead = recording.hideHead;
      this.frames = recording.frames;
    }

    this.costumeColor = this.isRecording
      ? recordCostumeColor
      : getCostumeColor(this.placementIndex);
    this.position = layout.getPosition(
      placementIndex,
      new THREE.Vector3(),
      !!recording || !!single
    );
    this.updatePosition();
    this.costumeColor = this.isRecording
      ? recordCostumeColor
      : getCostumeColor(this.index);
  }

  load(callback) {
    const frames = [];
    this.streamer = streamJSON(
      `${PROTOCOL}//storage.googleapis.com/you-move-me.appspot.com/recordings/${this.url}/${
        `${queryData.fps || (feature.has6DOF ? 45 : 15)}FPS.json`
      }`,
      (error, json) => {
        if (error || !json) {
          if (callback) {
            return callback(error);
          }
        }
        if (!this.frames) {
          // First JSON is meta object:
          const meta = JSON.parse(json);
          this.frames = frames;
          this.hideHead = meta.hideHead;
          this.performanceCount = meta.count;
          if (meta.fps) {
            this.fps = meta.fps;
          }
        } else {
          frames.push(json);
        }
      }
    );
  }

  updatePosition() {
    const position = tempVector()
      .add(this.position)
      .add(roomOffset);
    position.y -= 1;
    const type = layout.getType(this.placementIndex);
    const meshes = meshesByType[type] || meshesByType.HORIZONTAL;
    const color = getRoomColor(this.placementIndex);
    for (const i in meshes) {
      let mesh = meshes[i];
      let index = mesh.geometry.maxInstancedCount++;
      mesh.setPositionAt(index, position);
      mesh.setColorAt(index, color);
      mesh.needsUpdate();

      mesh = wallMeshes[i];
      if (layout.hasWall(this.placementIndex)) {
        index = mesh.geometry.maxInstancedCount++;
        mesh.setPositionAt(index, position);
        mesh.setColorAt(index, color);
        mesh.needsUpdate();
      }
    }
  }

  get worldPosition() {
    return this._worldPosition
      .copy(this.position)
      .applyMatrix4(roomsGroup.matrix);
  }

  isHighlighted(performance) {
    return Room.highlight.roomIndex === this.index
      && Room.highlight.performanceIndex === performance;
  }

  changeColor(color) {
    for (const i in roomMeshes) {
      const mesh = roomMeshes[i];
      mesh.setColorAt(this.index, color);
      mesh.needsUpdate('color');

      const wall = wallMeshes[i];
      wall.setColorAt(this.index, color);
      wall.needsUpdate('color');
    }
  }

  getHeadPosition(index, applyMatrix = true) {
    return this.getPositionImpl(index, 0, applyMatrix);
  }

  getRHandPosition(index, applyMatrix = true) {
    return this.getPositionImpl(index, 1, applyMatrix);
  }

  getLHandPosition(index, applyMatrix = true) {
    return this.getPositionImpl(index, 2, applyMatrix);
  }

  getPositionImpl(index, limbIndex, applyMatrix = true) {
    const position = serializer.avgPosition(
      this.lowerFrame,
      this.higherFrame,
      this.frameRatio,
      index,
      limbIndex,
      this.position
    );
    if (applyMatrix) {
      position.applyMatrix4(roomsGroup.matrix);
    }
    return position;
  }

  getHeadOrientation(index) {
    return serializer.avgQuaternion(
      this.lowerFrame,
      this.higherFrame,
      this.frameRatio,
      index,
      0
    );
  }

  transformToHead(object, layerIndex) {
    object.position.copy(this.getHeadPosition(layerIndex, false));
    object.quaternion.copy(this.getHeadOrientation(layerIndex));
  }

  get frame() {
    return this.frameNumber === undefined
      ? null
      : this.frames[Math.floor(this.frameNumber)];
  }

  secondsToFrame(seconds) {
    return (seconds % (audio.loopDuration * 2)) * this.fps;
  }

  gotoTime(seconds, maxLayers) {
    const { frames } = this;
    if (!frames) return;
    const frameNumber = this.frameNumber = this.secondsToFrame(seconds);
    const lower = Math.floor(frameNumber);
    const higher = Math.ceil(frameNumber);
    if (frames.length <= lower) return;
    const lowerFrame = this.lowerFrame = getFrame(frames, lower);
    const higherFrame = this.higherFrame = getFrame(frames, higher);
    const ratio = this.frameRatio = frameNumber % 1;
    if (maxLayers !== undefined) {
      this.performanceCount = Math.min(maxLayers, serializer.count(lowerFrame));
    } else if (!this.performanceCount) {
      this.performanceCount = serializer.count(lowerFrame);
    }

    // In orthographic mode, scale up the meshes:
    const scale = roomMesh === roomMeshes.orthographic ? 1.3 : 1;

    const { position } = this;
    for (let i = 0; i < this.performanceCount; i++) {
      const color = this.isHighlighted(i) ? highlightColor : this.costumeColor;
      if (!this.hideHead) {
        roomUtils.transformMesh(
          headMesh,
          lowerFrame,
          higherFrame,
          ratio,
          headMesh.geometry.maxInstancedCount++,
          i,
          0, // head
          scale,
          color,
          position
        );
      }
      roomUtils.transformMesh(
        handMesh,
        lowerFrame,
        higherFrame,
        ratio,
        handMesh.geometry.maxInstancedCount++,
        i,
        1, // first hand
        scale,
        color,
        position
      );
      roomUtils.transformMesh(
        handMesh,
        lowerFrame,
        higherFrame,
        ratio,
        handMesh.geometry.maxInstancedCount++,
        i,
        2, // second hand
        scale,
        color,
        position
      );
    }
  }

  destroy() {
    roomsGroup.remove(this.headMesh);
    roomsGroup.remove(this.handMesh);
    if (this.streamer) this.streamer.cancel();
  }
}

Room.clear = () => {
  handMesh.geometry.maxInstancedCount = headMesh.geometry.maxInstancedCount = 0;
};

Room.switchModel = (model) => {
  roomsGroup.remove(wallMesh);
  roomsGroup.remove(roomMesh);
  roomsGroup.remove(roomVerticalMesh);
  roomsGroup.remove(horizontalVerticalCornerMesh);
  roomsGroup.remove(verticalHorizontalCornerMesh);
  roomsGroup.remove(floorMesh);

  wallMesh = wallMeshes[model];
  roomMesh = roomMeshes[model];
  floorMesh = floorMeshes[model];
  roomVerticalMesh = roomVerticalMeshes[model];
  horizontalVerticalCornerMesh = horizontalVerticalCornerMeshes[model];
  verticalHorizontalCornerMesh = verticalHorizontalCornerMeshes[model];

  roomsGroup.add(wallMesh);
  roomsGroup.add(roomMesh);
  roomsGroup.add(roomVerticalMesh);
  roomsGroup.add(horizontalVerticalCornerMesh);
  roomsGroup.add(verticalHorizontalCornerMesh);
  roomsGroup.add(floorMesh);
};

Room.reset = ({ showAllWalls } = {}) => {
  Room.setHighlight();
  setIdentityMatrix(roomsGroup);
  if (roomMesh) roomsGroup.remove(roomMesh);
  if (wallMesh) roomsGroup.remove(wallMesh);
  if (headMesh) roomsGroup.remove(headMesh);
  if (handMesh) roomsGroup.remove(handMesh);

  roomIndex = 0;
  wallMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.wall.geometry,
      color: getRoomColor,
      material: props.wall.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.orthographicWall.geometry,
      color: getRoomColor,
      material: props.orthographicWall.material,
    }),
  };
  roomMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.room.geometry,
      color: getRoomColor,
      material: props.room.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.orthographicRoom.geometry,
      color: getRoomColor,
      material: props.orthographicRoom.material,
    }),
  };
  floorMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.floor.geometry,
      color: getRoomColor,
      material: props.floor.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.floor.geometry,
      color: getRoomColor,
      material: props.floor.material,
    }),
  };
  roomVerticalMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.verticalRoom.geometry,
      color: getRoomColor,
      material: props.verticalRoom.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.orthographicVerticalRoom.geometry,
      color: getRoomColor,
      material: props.orthographicVerticalRoom.material,
    }),
  };
  horizontalVerticalCornerMeshes = {
    default: createInstancedMesh({
      count: 1,
      geometry: props.horizontalVerticalCorner.geometry,
      color: getRoomColor,
      material: props.horizontalVerticalCorner.material,
    }),
    orthographic: createInstancedMesh({
      count: 1,
      geometry: props.orthographicHorizontalVerticalCorner.geometry,
      color: getRoomColor,
      material: props.orthographicHorizontalVerticalCorner.material,
    }),
  };
  verticalHorizontalCornerMeshes = {
    default: createInstancedMesh({
      count: 1,
      geometry: props.verticalHorizontalCorner.geometry,
      color: getRoomColor,
      material: props.verticalHorizontalCorner.material,
    }),
    orthographic: createInstancedMesh({
      count: 1,
      geometry: props.orthographicVerticalHorizontalCorner.geometry,
      color: getRoomColor,
      material: props.orthographicVerticalHorizontalCorner.material,
    }),
  };
  [
    horizontalVerticalCornerMeshes,
    verticalHorizontalCornerMeshes,
    roomVerticalMeshes,
    floorMeshes,
    roomMeshes,
    wallMeshes,
  ].forEach(meshes => {
    meshes.default.geometry.maxInstancedCount = 0;
    meshes.orthographic.geometry.maxInstancedCount = 0;
  });

  roomMeshes.default.receiveShadow = true;
  roomMeshes.orthographic.receiveShadow = true;

  wallMesh = wallMeshes.default;
  roomMesh = roomMeshes.default;
  floorMesh = floorMeshes.default;
  roomVerticalMesh = roomMeshes.default;

  headMesh = createInstancedMesh({
    count: layout.roomCount * 10,
    geometry: props.head.geometry,
  });
  headMesh.geometry.maxInstancedCount = 0;

  handMesh = createInstancedMesh({
    count: layout.roomCount * 10 * 2,
    geometry: props.hand.geometry,
  });
  handMesh.geometry.maxInstancedCount = 0;

  meshesByType = {
    HORIZONTAL: roomMeshes,
    VERTICAL: roomVerticalMeshes,
    HORIZONTAL_CORNER: horizontalVerticalCornerMeshes,
    VERTICAL_CORNER: verticalHorizontalCornerMeshes,
    PLANE: floorMeshes,
  };

  roomsGroup.add(headMesh);
  roomsGroup.add(handMesh);
  roomsGroup.add(wallMesh);
  roomsGroup.add(roomMesh);
  viewer.scene.add(roomsGroup);

  // Move an extra invisible object3d with a texture to the end of scene's children
  // array in order to solve a texture glitch as described in:
  // https://github.com/puckey/you-move-me/issues/129
  viewer.scene.add(debugMesh);
};

Room.rotate180 = () => {
  set180RotationMatrix(roomsGroup);
};

Room.group = roomsGroup;

Room.highlight = {};

Room.setHighlight = ([room, performance] = []) => {
  Room.highlight.roomIndex = room;
  Room.highlight.performanceIndex = performance;
};
<commit_msg>cms: add wall to single room<commit_after>import * as THREE from '../lib/three';

import { queryData } from '../utils/url';
import props from '../props';
import {
  tempVector,
  createInstancedMesh,
  setIdentityMatrix,
  set180RotationMatrix,
} from '../utils/three';
import viewer from '../viewer';
import settings from '../settings';
import {
  getCostumeColor,
  getRoomColor,
  recordCostumeColor,
  highlightColor,
} from '../theme/colors';
import streamJSON from '../lib/stream-json';
import * as roomUtils from './utils';
import layout from './layout';
import dummyTextureUrl from '../public/dummy.png';
import audio from '../audio';
import * as serializer from '../utils/serializer';
import feature from '../utils/feature';

const PROTOCOL = location.protocol;

let roomIndex = 0;
let wallMesh;
let wallMeshes;
let roomMesh;
let floorMesh;
let roomVerticalMesh;
let horizontalVerticalCornerMesh;
let verticalHorizontalCornerMesh;
let roomMeshes;
let floorMeshes;
let roomVerticalMeshes;
let horizontalVerticalCornerMeshes;
let verticalHorizontalCornerMeshes;
let headMesh;
let handMesh;

let meshesByType;

const roomOffset = new THREE.Vector3(0, settings.roomHeight * 0.5, 0);
const roomsGroup = new THREE.Group();
roomsGroup.matrixAutoUpdate = false;

const debugMesh = new THREE.Mesh(
  new THREE.BoxGeometry(0, 0, 0),
  new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load(dummyTextureUrl),
  })
);
debugMesh.frustumCulled = false;

const getFrame = (frames, number) => {
  let frame = frames[number];
  if (!frame) frame--;
  // Check if data is still a string:
  if (frame[0] === '[') {
    frame = frames[number] = JSON.parse(frame);
  }
  return frame;
};

export default class Room {
  constructor({ url, recording, index, pathRecording, single }) {
    this._worldPosition = new THREE.Vector3();
    const placementIndex = this.placementIndex = index === undefined
      ? roomIndex
      : index;
    this.index = roomIndex;
    roomIndex += 1;

    this.hasWall = !!single || layout.hasWall(this.placementIndex);
    this.isRecording = !!recording;
    if (url) {
       // remove everything after '?':
      this.url = (url.indexOf('?') === -1) ? url : url.split('?')[0];
    }
    this.pathRecording = pathRecording;
    this.fps = 90;
    if (recording) {
      this.hideHead = recording.hideHead;
      this.frames = recording.frames;
    }

    this.costumeColor = this.isRecording
      ? recordCostumeColor
      : getCostumeColor(this.placementIndex);
    this.position = layout.getPosition(
      placementIndex,
      new THREE.Vector3(),
      !!recording || !!single
    );
    this.updatePosition();
    this.costumeColor = this.isRecording
      ? recordCostumeColor
      : getCostumeColor(this.index);
  }

  load(callback) {
    const frames = [];
    this.streamer = streamJSON(
      `${PROTOCOL}//storage.googleapis.com/you-move-me.appspot.com/recordings/${this.url}/${
        `${queryData.fps || (feature.has6DOF ? 45 : 15)}FPS.json`
      }`,
      (error, json) => {
        if (error || !json) {
          if (callback) {
            return callback(error);
          }
        }
        if (!this.frames) {
          // First JSON is meta object:
          const meta = JSON.parse(json);
          this.frames = frames;
          this.hideHead = meta.hideHead;
          this.performanceCount = meta.count;
          if (meta.fps) {
            this.fps = meta.fps;
          }
        } else {
          frames.push(json);
        }
      }
    );
  }

  updatePosition() {
    const position = tempVector()
      .add(this.position)
      .add(roomOffset);
    position.y -= 1;
    const type = layout.getType(this.placementIndex);
    const meshes = meshesByType[type] || meshesByType.HORIZONTAL;
    const color = getRoomColor(this.placementIndex);
    for (const i in meshes) {
      let mesh = meshes[i];
      let index = mesh.geometry.maxInstancedCount++;
      mesh.setPositionAt(index, position);
      mesh.setColorAt(index, color);
      mesh.needsUpdate();

      mesh = wallMeshes[i];
      if (this.hasWall) {
        index = mesh.geometry.maxInstancedCount++;
        mesh.setPositionAt(index, position);
        mesh.setColorAt(index, color);
        mesh.needsUpdate();
      }
    }
  }

  get worldPosition() {
    return this._worldPosition
      .copy(this.position)
      .applyMatrix4(roomsGroup.matrix);
  }

  isHighlighted(performance) {
    return Room.highlight.roomIndex === this.index
      && Room.highlight.performanceIndex === performance;
  }

  changeColor(color) {
    for (const i in roomMeshes) {
      const mesh = roomMeshes[i];
      mesh.setColorAt(this.index, color);
      mesh.needsUpdate('color');

      const wall = wallMeshes[i];
      wall.setColorAt(this.index, color);
      wall.needsUpdate('color');
    }
  }

  getHeadPosition(index, applyMatrix = true) {
    return this.getPositionImpl(index, 0, applyMatrix);
  }

  getRHandPosition(index, applyMatrix = true) {
    return this.getPositionImpl(index, 1, applyMatrix);
  }

  getLHandPosition(index, applyMatrix = true) {
    return this.getPositionImpl(index, 2, applyMatrix);
  }

  getPositionImpl(index, limbIndex, applyMatrix = true) {
    const position = serializer.avgPosition(
      this.lowerFrame,
      this.higherFrame,
      this.frameRatio,
      index,
      limbIndex,
      this.position
    );
    if (applyMatrix) {
      position.applyMatrix4(roomsGroup.matrix);
    }
    return position;
  }

  getHeadOrientation(index) {
    return serializer.avgQuaternion(
      this.lowerFrame,
      this.higherFrame,
      this.frameRatio,
      index,
      0
    );
  }

  transformToHead(object, layerIndex) {
    object.position.copy(this.getHeadPosition(layerIndex, false));
    object.quaternion.copy(this.getHeadOrientation(layerIndex));
  }

  get frame() {
    return this.frameNumber === undefined
      ? null
      : this.frames[Math.floor(this.frameNumber)];
  }

  secondsToFrame(seconds) {
    return (seconds % (audio.loopDuration * 2)) * this.fps;
  }

  gotoTime(seconds, maxLayers) {
    const { frames } = this;
    if (!frames) return;
    const frameNumber = this.frameNumber = this.secondsToFrame(seconds);
    const lower = Math.floor(frameNumber);
    const higher = Math.ceil(frameNumber);
    if (frames.length <= lower) return;
    const lowerFrame = this.lowerFrame = getFrame(frames, lower);
    const higherFrame = this.higherFrame = getFrame(frames, higher);
    const ratio = this.frameRatio = frameNumber % 1;
    if (maxLayers !== undefined) {
      this.performanceCount = Math.min(maxLayers, serializer.count(lowerFrame));
    } else if (!this.performanceCount) {
      this.performanceCount = serializer.count(lowerFrame);
    }

    // In orthographic mode, scale up the meshes:
    const scale = roomMesh === roomMeshes.orthographic ? 1.3 : 1;

    const { position } = this;
    for (let i = 0; i < this.performanceCount; i++) {
      const color = this.isHighlighted(i) ? highlightColor : this.costumeColor;
      if (!this.hideHead) {
        roomUtils.transformMesh(
          headMesh,
          lowerFrame,
          higherFrame,
          ratio,
          headMesh.geometry.maxInstancedCount++,
          i,
          0, // head
          scale,
          color,
          position
        );
      }
      roomUtils.transformMesh(
        handMesh,
        lowerFrame,
        higherFrame,
        ratio,
        handMesh.geometry.maxInstancedCount++,
        i,
        1, // first hand
        scale,
        color,
        position
      );
      roomUtils.transformMesh(
        handMesh,
        lowerFrame,
        higherFrame,
        ratio,
        handMesh.geometry.maxInstancedCount++,
        i,
        2, // second hand
        scale,
        color,
        position
      );
    }
  }

  destroy() {
    roomsGroup.remove(this.headMesh);
    roomsGroup.remove(this.handMesh);
    if (this.streamer) this.streamer.cancel();
  }
}

Room.clear = () => {
  handMesh.geometry.maxInstancedCount = headMesh.geometry.maxInstancedCount = 0;
};

Room.switchModel = (model) => {
  roomsGroup.remove(wallMesh);
  roomsGroup.remove(roomMesh);
  roomsGroup.remove(roomVerticalMesh);
  roomsGroup.remove(horizontalVerticalCornerMesh);
  roomsGroup.remove(verticalHorizontalCornerMesh);
  roomsGroup.remove(floorMesh);

  wallMesh = wallMeshes[model];
  roomMesh = roomMeshes[model];
  floorMesh = floorMeshes[model];
  roomVerticalMesh = roomVerticalMeshes[model];
  horizontalVerticalCornerMesh = horizontalVerticalCornerMeshes[model];
  verticalHorizontalCornerMesh = verticalHorizontalCornerMeshes[model];

  roomsGroup.add(wallMesh);
  roomsGroup.add(roomMesh);
  roomsGroup.add(roomVerticalMesh);
  roomsGroup.add(horizontalVerticalCornerMesh);
  roomsGroup.add(verticalHorizontalCornerMesh);
  roomsGroup.add(floorMesh);
};

Room.reset = ({ showAllWalls } = {}) => {
  Room.setHighlight();
  setIdentityMatrix(roomsGroup);
  if (roomMesh) roomsGroup.remove(roomMesh);
  if (wallMesh) roomsGroup.remove(wallMesh);
  if (headMesh) roomsGroup.remove(headMesh);
  if (handMesh) roomsGroup.remove(handMesh);

  roomIndex = 0;
  wallMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.wall.geometry,
      color: getRoomColor,
      material: props.wall.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.orthographicWall.geometry,
      color: getRoomColor,
      material: props.orthographicWall.material,
    }),
  };
  roomMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.room.geometry,
      color: getRoomColor,
      material: props.room.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.orthographicRoom.geometry,
      color: getRoomColor,
      material: props.orthographicRoom.material,
    }),
  };
  floorMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.floor.geometry,
      color: getRoomColor,
      material: props.floor.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.floor.geometry,
      color: getRoomColor,
      material: props.floor.material,
    }),
  };
  roomVerticalMeshes = {
    default: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.verticalRoom.geometry,
      color: getRoomColor,
      material: props.verticalRoom.material,
    }),
    orthographic: createInstancedMesh({
      count: layout.roomCount,
      geometry: props.orthographicVerticalRoom.geometry,
      color: getRoomColor,
      material: props.orthographicVerticalRoom.material,
    }),
  };
  horizontalVerticalCornerMeshes = {
    default: createInstancedMesh({
      count: 1,
      geometry: props.horizontalVerticalCorner.geometry,
      color: getRoomColor,
      material: props.horizontalVerticalCorner.material,
    }),
    orthographic: createInstancedMesh({
      count: 1,
      geometry: props.orthographicHorizontalVerticalCorner.geometry,
      color: getRoomColor,
      material: props.orthographicHorizontalVerticalCorner.material,
    }),
  };
  verticalHorizontalCornerMeshes = {
    default: createInstancedMesh({
      count: 1,
      geometry: props.verticalHorizontalCorner.geometry,
      color: getRoomColor,
      material: props.verticalHorizontalCorner.material,
    }),
    orthographic: createInstancedMesh({
      count: 1,
      geometry: props.orthographicVerticalHorizontalCorner.geometry,
      color: getRoomColor,
      material: props.orthographicVerticalHorizontalCorner.material,
    }),
  };
  [
    horizontalVerticalCornerMeshes,
    verticalHorizontalCornerMeshes,
    roomVerticalMeshes,
    floorMeshes,
    roomMeshes,
    wallMeshes,
  ].forEach(meshes => {
    meshes.default.geometry.maxInstancedCount = 0;
    meshes.orthographic.geometry.maxInstancedCount = 0;
  });

  roomMeshes.default.receiveShadow = true;
  roomMeshes.orthographic.receiveShadow = true;

  wallMesh = wallMeshes.default;
  roomMesh = roomMeshes.default;
  floorMesh = floorMeshes.default;
  roomVerticalMesh = roomMeshes.default;

  headMesh = createInstancedMesh({
    count: layout.roomCount * 10,
    geometry: props.head.geometry,
  });
  headMesh.geometry.maxInstancedCount = 0;

  handMesh = createInstancedMesh({
    count: layout.roomCount * 10 * 2,
    geometry: props.hand.geometry,
  });
  handMesh.geometry.maxInstancedCount = 0;

  meshesByType = {
    HORIZONTAL: roomMeshes,
    VERTICAL: roomVerticalMeshes,
    HORIZONTAL_CORNER: horizontalVerticalCornerMeshes,
    VERTICAL_CORNER: verticalHorizontalCornerMeshes,
    PLANE: floorMeshes,
  };

  roomsGroup.add(headMesh);
  roomsGroup.add(handMesh);
  roomsGroup.add(wallMesh);
  roomsGroup.add(roomMesh);
  viewer.scene.add(roomsGroup);

  // Move an extra invisible object3d with a texture to the end of scene's children
  // array in order to solve a texture glitch as described in:
  // https://github.com/puckey/you-move-me/issues/129
  viewer.scene.add(debugMesh);
};

Room.rotate180 = () => {
  set180RotationMatrix(roomsGroup);
};

Room.group = roomsGroup;

Room.highlight = {};

Room.setHighlight = ([room, performance] = []) => {
  Room.highlight.roomIndex = room;
  Room.highlight.performanceIndex = performance;
};
<|endoftext|>