<commit_before>var url = require('url');
var fs = require('fs');
var formidable = require('formidable');
var browser_fingerprint = require('browser_fingerprint');
var Mime = require('mime');

var web = function(api, options, next){

  //////////
  // INIT //
  //////////

  var type = 'web'
  var attributes = {
    canChat: false,
    logConnections: false,
    logExits: false,
    sendWelcomeMessage: false,
    verbs: [
      // no verbs for connections of this type, as they are to be very short-lived
    ]
  }

  var server = new api.genericServer(type, options, attributes);

  if(['api', 'file'].indexOf(api.config.servers.web.rootEndpointType) < 0){
    server.log('api.config.servers.web.rootEndpointType can only be \'api\' or \'file\'', 'emerg');
    process.exit();
  }
  if(api.config.servers.web.flatFileCacheDuration == null){
    api.config.servers.web.flatFileCacheDuration = 0;
  }
  if(api.config.servers.web.directoryFileType == null){
    api.config.servers.web.directoryFileType = 'index.html';
  }

  //////////////////////
  // REQUIRED METHODS //
  //////////////////////

  server._start = function(next){
    if(options.secure == false){
      var http = require('http');
      server.server = http.createServer(function(req, res){
        handleRequest(req, res);
      });
    } else {
      var https = require('https');
      server.server = https.createServer(api.config.servers.web.serverOptions, function(req, res){
        handleRequest(req, res);
      });
    }

    server.server.on('error', function(e){
      server.log('cannot start web server @ ' + options.bindIP + ':' + options.port + '; exiting.', 'emerg');
      server.log(e, 'error');
      process.exit(1);
    });

    server.server.listen(options.port, options.bindIP, function(){
      next(server);
    });
  }

  server._stop = function(next){
    server.server.close();
    process.nextTick(function(){
      next();
    });
  }

  server.sendMessage = function(connection, message){
    var stringResponse = '';
    if(connection.rawConnection.req.method.toUpperCase() != 'HEAD'){
      stringResponse = String(message);
    }
    connection.rawConnection.responseHeaders.push(['Content-Length', Buffer.byteLength(stringResponse, 'utf8')]);
    cleanHeaders(connection);
    var headers = connection.rawConnection.responseHeaders;
    var responseHttpCode = parseInt(connection.rawConnection.responseHttpCode);
    connection.rawConnection.res.writeHead(responseHttpCode, headers);
    connection.rawConnection.res.end(stringResponse);
    server.destroyConnection(connection);
  }

  server.sendFile = function(connection, error, fileStream, mime, length){
    connection.rawConnection.responseHeaders.push(['Content-Type', mime]);
    connection.rawConnection.responseHeaders.push(['Content-Length', length]); // Don't send both content-length and transfer-encoding
    // connection.rawConnection.responseHeaders.push(['Transfer-Encoding', 'Chunked'])
    connection.rawConnection.responseHeaders.push(['Expires', new Date(new Date().getTime() + api.config.servers.web.flatFileCacheDuration * 1000).toUTCString()]);
    connection.rawConnection.responseHeaders.push(['Cache-Control', 'max-age=' + api.config.servers.web.flatFileCacheDuration + ', must-revalidate']);
    cleanHeaders(connection);
    var headers = connection.rawConnection.responseHeaders;
    if(error != null){ connection.rawConnection.responseHttpCode = 404 }
    var responseHttpCode = parseInt(connection.rawConnection.responseHttpCode);
    connection.rawConnection.res.writeHead(responseHttpCode, headers);
    if(error != null){
      connection.rawConnection.res.end(String(error));
      server.destroyConnection(connection);
    } else {
      fileStream.pipe(connection.rawConnection.res);
      fileStream.on('end', function(){
        server.destroyConnection(connection);
      });
    }
  };

  ////////////
  // EVENTS //
  ////////////

  server.on('connection', function(connection){
    determineRequestParams(connection, function(requestMode){
      if(requestMode == 'api'){
        server.processAction(connection);
      } else if(requestMode == 'file'){
        server.processFile(connection);
      } else if(requestMode == 'options'){
        respondToOptions(connection);
      } else if(requestMode == 'trace'){
        respondToTrace(connection);
      }
    });
  });

  server.on('actionComplete', function(connection, toRender, messageCount){
    completeResponse(connection, toRender, messageCount);
  });

  /////////////
  // HELPERS //
  /////////////

  var handleRequest = function(req, res){
    browser_fingerprint.fingerprint(req, api.config.servers.web.fingerprintOptions, function(fingerprint, elementHash, cookieHash){
      var responseHeaders = []
      var cookies =  api.utils.parseCookies(req);
      var responseHttpCode = 200;
      var method = req.method;
      var parsedURL = url.parse(req.url, true);
      var i;
      for(i in cookieHash){
        responseHeaders.push([i, cookieHash[i]]);
      }

      // https://github.com/evantahler/actionhero/issues/189
      responseHeaders.push(['Content-Type', 'application/json; charset=utf-8']);

      if(typeof(api.config.servers.web.httpHeaders) != null){
        for(i in api.config.servers.web.httpHeaders){
          responseHeaders.push([i, api.config.servers.web.httpHeaders[i]]);
        }
      }

      var remoteIP = req.connection.remoteAddress;
      var remotePort = req.connection.remotePort;

      if(req.headers['x-forwarded-for'] != null){
        var forwardedIp = req.headers['x-forwarded-for'].split(",")[0];
        if(forwardedIp.indexOf(".") >= 0 || (forwardedIp.indexOf(".") < 0 && forwardedIp.indexOf(":") < 0)){
          // IPv4
          forwardedIp = forwardedIp.replace('::ffff:',''); // remove any IPv6 information, ie: '::ffff:127.0.0.1'
          var parts = forwardedIp.split(":");
          if(parts[0] != null){ remoteIP = parts[0]; }
          if(parts[1] != null){ remotePort = parts[1]; }
        }else{
          // IPv6
          var parts = api.utils.parseIPv6URI(forwardedIp);
          if(parts['host'] != null){ remoteIP = parts['host']; }
          if(parts['port'] != null){ remotePort = parts['port']; }
        }

        if(req.headers['x-forwarded-port'] != null){
          remotePort = req.headers['x-forwarded-port'];
        }
      }

      server.buildConnection({
      // will emit 'connection'
        rawConnection: {
          req: req,
          res: res,
          method: method,
          cookies: cookies,
          responseHeaders: responseHeaders,
          responseHttpCode: responseHttpCode,
          parsedURL: parsedURL
        },
        id: fingerprint,
        remoteAddress: remoteIP,
        remotePort: remotePort
      });
    });
  }

  var completeResponse = function(connection, toRender, messageCount){
    if(toRender === true){
      if(api.config.servers.web.metadataOptions.serverInformation){
        var stopTime = new Date().getTime();
        connection.response.serverInformation = {
          serverName:      api.config.general.serverName,
          apiVersion:      api.config.general.apiVersion,
          requestDuration: (stopTime - connection.connectedAt),
          currentTime:     stopTime
        };
      }

      if(api.config.servers.web.metadataOptions.requesterInformation){
        connection.response.requesterInformation = buildRequesterInformation(connection);
      }

      if(connection.response.error != null){
        if(shouldSendDocumentation(connection)){
          connection.response.documentation = api.documentation.documentation;
          delete connection.error;
          delete connection.response.error;
        } else if(api.config.servers.web.returnErrorCodes == true && connection.rawConnection.responseHttpCode == 200){
          if(connection.action == '{no action}' || String(connection.error).indexOf('is not a known action or that is not a valid apiVersion.') > 0){
            connection.rawConnection.responseHttpCode = 404;
          } else if(String(connection.error).indexOf('is a required parameter for this action') > 0){
            connection.rawConnection.responseHttpCode = 422;
          } else if(String(connection.error).indexOf('none of the required params for this action were provided') > 0){
            connection.rawConnection.responseHttpCode = 422;
          } else if('Error: ' +  api.config.general.serverErrorMessage == String(connection.response.error)){
            connection.rawConnection.responseHttpCode = 500;
          } else {
            connection.rawConnection.responseHttpCode = 400;
          }
        }
      }

      var stringResponse = JSON.stringify(connection.response, null, 2);
      if(connection.response.error == null &&
         connection.action != null &&
         connection.params.apiVersion != null &&
         api.actions.actions[connection.action][connection.params.apiVersion].matchExtensionMimeType === true
        ){
        connection.rawConnection.responseHeaders.push(['Content-Type', Mime.lookup(connection.extension)]);
      }
      if(connection.params.callback != null){
        connection.rawConnection.responseHeaders.push(['Content-Type', 'application/javascript']);
        stringResponse = connection.params.callback + '(' + stringResponse + ');';
      }

      server.sendMessage(connection, stringResponse);
    }
  }

  var connectionHasHeader = function(connection, match){
    for(var i in connection.rawConnection.responseHeaders){
      if(connection.rawConnection.responseHeaders[i][0].toLowerCase() === match.toLowerCase()){
        return true;
      }
    }
    return false;
  }

  var respondToOptions = function(connection){
    if(api.config.servers.web.httpHeaders['Access-Control-Allow-Methods'] == null && !connectionHasHeader(connection, 'Access-Control-Allow-Methods')){
      var methods = 'HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE';
      connection.rawConnection.responseHeaders.push(['Access-Control-Allow-Methods', methods]);
    }
    if(api.config.servers.web.httpHeaders['Access-Control-Allow-Origin'] == null && !connectionHasHeader(connection, 'Access-Control-Allow-Origin')){
      var origin = '*';
      connection.rawConnection.responseHeaders.push(['Access-Control-Allow-Origin', origin]);
    }
    server.sendMessage(connection, '');
  }

  var respondToTrace= function(connection){
    var data = buildRequesterInformation(connection);
    var stringResponse = JSON.stringify(data, null, 2);
    server.sendMessage(connection, stringResponse);
  }

  var determineRequestParams = function(connection, callback){
    var requestMode = api.config.servers.web.rootEndpointType; // api or public
    var pathParts = connection.rawConnection.parsedURL.pathname.split('/');
    var apiPathParts = connection.rawConnection.parsedURL.pathname.split('/');
    var filePathParts = connection.rawConnection.parsedURL.pathname.split('/');
    var extensionParts = connection.rawConnection.parsedURL.pathname.split('.');
    if (extensionParts.length > 1){
      connection.extension = extensionParts[(extensionParts.length - 1)];
    }
    filePathParts.shift();
    apiPathParts.shift();
    var i;
    if(pathParts.length > 0){
      if(pathParts[1] == api.config.servers.web.urlPathForActions){
        requestMode = 'api';
        apiPathParts.shift();
      } else if(pathParts[1] == api.config.servers.web.urlPathForFiles || connection.rawConnection.parsedURL.pathname.indexOf(api.config.servers.web.urlPathForFiles) === 0){
        requestMode = 'file';
        filePathParts.shift();
        i = 1;
        while(i < api.config.servers.web.urlPathForFiles.split('/').length - 1){
          filePathParts.shift();
          i++;
        }
      }
    }
    fillParamsFromWebRequest(connection, connection.rawConnection.parsedURL.query); // GET, PUT, and DELETE params
    if(requestMode == 'api'){
      var httpMethod = connection.rawConnection.req.method.toUpperCase();
      if(httpMethod == 'OPTIONS'){
        requestMode = 'options'
        callback(requestMode);
      }else if(httpMethod == 'GET' || httpMethod == 'HEAD'){
        api.routes.processRoute(connection);
        if(connection.params['action'] == null){ connection.params['action'] = apiPathParts[0]; }
        callback(requestMode);
      } else { // POST/DELETE/PUT params
        if(httpMethod == 'TRACE'){ requestMode = 'trace'; }
        if(connection.rawConnection.req.headers['content-type'] == null && connection.rawConnection.req.headers['Content-Type'] == null){
          // not a legal post; bad headers
          api.routes.processRoute(connection);
          if(connection.params['action'] == null){ connection.params['action'] = apiPathParts[0]; }
          callback(requestMode);
        } else {
          var form = new formidable.IncomingForm();
          for(i in api.config.servers.web.formOptions){
            form[i] = api.config.servers.web.formOptions[i];
          }
          form.parse(connection.rawConnection.req, function(err, fields, files) {
            if(err){
              server.log('error processing form: ' + String(err), 'error');
              connection.error = new Error('There was an error processing this form.');
            } else {
              fillParamsFromWebRequest(connection, files);
              fillParamsFromWebRequest(connection, fields);
            }
            api.routes.processRoute(connection);
            if(connection.params['action'] == null){ connection.params['action'] = apiPathParts[0]; }
            callback(requestMode);
          });
        }
      }
    } else {
      if(connection.params['file'] == null){
        connection.params['file'] = filePathParts.join('/');
        if(connection.params['file'] == '' || connection.params['file'][connection.params['file'].length - 1] == '/'){
          connection.params['file'] = connection.params['file'] + api.config.servers.web.directoryFileType;
        }
      }
      callback(requestMode);
    }
  }

  var fillParamsFromWebRequest = function(connection, varsHash){
    // helper for JSON posts
    var collapsedVarsHash = api.utils.collapseObjectToArray(varsHash);
    if(collapsedVarsHash !== false){
      varsHash = {payload: collapsedVarsHash} // post was an array, lets call it "payload"
    }else if(api.utils.hashLength(varsHash) == 1){
      var key = Object.keys(varsHash)[0];
    }

    for(var v in varsHash){
      connection.params[v] = varsHash[v];
    };
  }

  var shouldSendDocumentation = function(connection){
    if(connection.action != '{no action}'){ return false }
    if(connection.rawConnection.req.method.toUpperCase() != 'GET'){ return false }
    var params = api.utils.objClone(connection.params);
    delete params.action;
    delete params.limit;
    delete params.offset;
    delete params[''];
    return (api.utils.hashLength(params) === 0);
  }

  var buildRequesterInformation = function(connection){
    var requesterInformation = {
      id: connection.id,
      remoteIP: connection.remoteIP,
      receivedParams: {}
    };

    for(var p in connection.params){
      if(api.config.general.disableParamScrubbing === true || api.params.postVariables.indexOf(p) >= 0){
        requesterInformation.receivedParams[p] = connection.params[p];
      }
    }

    return requesterInformation;
  }

  var cleanHeaders = function(connection){
    var originalHeaders = connection.rawConnection.responseHeaders.reverse();
    var foundHeaders = [];
    var cleanedHeaders = [];
    for(var i in originalHeaders){
      var key = originalHeaders[i][0];
      var value = originalHeaders[i][1];
      if(foundHeaders.indexOf(key.toLowerCase()) >= 0 && key.toLowerCase().indexOf('set-cookie') < 0 ){
        // ignore, it's a duplicate
      } else if(connection.rawConnection.req.method.toUpperCase() == 'HEAD' && key == 'Transfer-Encoding'){
        // ignore, we can't send this header for HEAD requests
      } else {
        foundHeaders.push(key.toLowerCase());
        cleanedHeaders.push([key, value]);
      }
    }
    connection.rawConnection.responseHeaders = cleanedHeaders;
  }

  next(server);
}

/////////////////////////////////////////////////////////////////////
// exports
exports.web = web;
<commit_msg>allow helpers for when unix sockets don't pass bind information<commit_after>var url = require('url');
var fs = require('fs');
var formidable = require('formidable');
var browser_fingerprint = require('browser_fingerprint');
var Mime = require('mime');

var web = function(api, options, next){

  //////////
  // INIT //
  //////////

  var type = 'web'
  var attributes = {
    canChat: false,
    logConnections: false,
    logExits: false,
    sendWelcomeMessage: false,
    verbs: [
      // no verbs for connections of this type, as they are to be very short-lived
    ]
  }

  var server = new api.genericServer(type, options, attributes);

  if(['api', 'file'].indexOf(api.config.servers.web.rootEndpointType) < 0){
    server.log('api.config.servers.web.rootEndpointType can only be \'api\' or \'file\'', 'emerg');
    process.exit();
  }
  if(api.config.servers.web.flatFileCacheDuration == null){
    api.config.servers.web.flatFileCacheDuration = 0;
  }
  if(api.config.servers.web.directoryFileType == null){
    api.config.servers.web.directoryFileType = 'index.html';
  }

  //////////////////////
  // REQUIRED METHODS //
  //////////////////////

  server._start = function(next){
    if(options.secure == false){
      var http = require('http');
      server.server = http.createServer(function(req, res){
        handleRequest(req, res);
      });
    } else {
      var https = require('https');
      server.server = https.createServer(api.config.servers.web.serverOptions, function(req, res){
        handleRequest(req, res);
      });
    }

    server.server.on('error', function(e){
      server.log('cannot start web server @ ' + options.bindIP + ':' + options.port + '; exiting.', 'emerg');
      server.log(e, 'error');
      process.exit(1);
    });

    server.server.listen(options.port, options.bindIP, function(){
      next(server);
    });
  }

  server._stop = function(next){
    server.server.close();
    process.nextTick(function(){
      next();
    });
  }

  server.sendMessage = function(connection, message){
    var stringResponse = '';
    if(connection.rawConnection.req.method.toUpperCase() != 'HEAD'){
      stringResponse = String(message);
    }
    connection.rawConnection.responseHeaders.push(['Content-Length', Buffer.byteLength(stringResponse, 'utf8')]);
    cleanHeaders(connection);
    var headers = connection.rawConnection.responseHeaders;
    var responseHttpCode = parseInt(connection.rawConnection.responseHttpCode);
    connection.rawConnection.res.writeHead(responseHttpCode, headers);
    connection.rawConnection.res.end(stringResponse);
    server.destroyConnection(connection);
  }

  server.sendFile = function(connection, error, fileStream, mime, length){
    connection.rawConnection.responseHeaders.push(['Content-Type', mime]);
    connection.rawConnection.responseHeaders.push(['Content-Length', length]); // Don't send both content-length and transfer-encoding
    // connection.rawConnection.responseHeaders.push(['Transfer-Encoding', 'Chunked'])
    connection.rawConnection.responseHeaders.push(['Expires', new Date(new Date().getTime() + api.config.servers.web.flatFileCacheDuration * 1000).toUTCString()]);
    connection.rawConnection.responseHeaders.push(['Cache-Control', 'max-age=' + api.config.servers.web.flatFileCacheDuration + ', must-revalidate']);
    cleanHeaders(connection);
    var headers = connection.rawConnection.responseHeaders;
    if(error != null){ connection.rawConnection.responseHttpCode = 404 }
    var responseHttpCode = parseInt(connection.rawConnection.responseHttpCode);
    connection.rawConnection.res.writeHead(responseHttpCode, headers);
    if(error != null){
      connection.rawConnection.res.end(String(error));
      server.destroyConnection(connection);
    } else {
      fileStream.pipe(connection.rawConnection.res);
      fileStream.on('end', function(){
        server.destroyConnection(connection);
      });
    }
  };

  ////////////
  // EVENTS //
  ////////////

  server.on('connection', function(connection){
    determineRequestParams(connection, function(requestMode){
      if(requestMode == 'api'){
        server.processAction(connection);
      } else if(requestMode == 'file'){
        server.processFile(connection);
      } else if(requestMode == 'options'){
        respondToOptions(connection);
      } else if(requestMode == 'trace'){
        respondToTrace(connection);
      }
    });
  });

  server.on('actionComplete', function(connection, toRender, messageCount){
    completeResponse(connection, toRender, messageCount);
  });

  /////////////
  // HELPERS //
  /////////////

  var handleRequest = function(req, res){
    browser_fingerprint.fingerprint(req, api.config.servers.web.fingerprintOptions, function(fingerprint, elementHash, cookieHash){
      var responseHeaders = []
      var cookies =  api.utils.parseCookies(req);
      var responseHttpCode = 200;
      var method = req.method;
      var parsedURL = url.parse(req.url, true);
      var i;
      for(i in cookieHash){
        responseHeaders.push([i, cookieHash[i]]);
      }

      // https://github.com/evantahler/actionhero/issues/189
      responseHeaders.push(['Content-Type', 'application/json; charset=utf-8']);

      if(typeof(api.config.servers.web.httpHeaders) != null){
        for(i in api.config.servers.web.httpHeaders){
          responseHeaders.push([i, api.config.servers.web.httpHeaders[i]]);
        }
      }

      var remoteIP = req.connection.remoteAddress;
      var remotePort = req.connection.remotePort;

      // helpers for unix socket bindings with no forward
      if(remoteIP == null && remotePort == null){
        remoteIP   = '0.0.0.0';
        remotePort = '0';
      }

      if(req.headers['x-forwarded-for'] != null){
        var forwardedIp = req.headers['x-forwarded-for'].split(",")[0];
        if(forwardedIp.indexOf(".") >= 0 || (forwardedIp.indexOf(".") < 0 && forwardedIp.indexOf(":") < 0)){
          // IPv4
          forwardedIp = forwardedIp.replace('::ffff:',''); // remove any IPv6 information, ie: '::ffff:127.0.0.1'
          var parts = forwardedIp.split(":");
          if(parts[0] != null){ remoteIP = parts[0]; }
          if(parts[1] != null){ remotePort = parts[1]; }
        }else{
          // IPv6
          var parts = api.utils.parseIPv6URI(forwardedIp);
          if(parts['host'] != null){ remoteIP = parts['host']; }
          if(parts['port'] != null){ remotePort = parts['port']; }
        }

        if(req.headers['x-forwarded-port'] != null){
          remotePort = req.headers['x-forwarded-port'];
        }
      }

      server.buildConnection({
      // will emit 'connection'
        rawConnection: {
          req: req,
          res: res,
          method: method,
          cookies: cookies,
          responseHeaders: responseHeaders,
          responseHttpCode: responseHttpCode,
          parsedURL: parsedURL
        },
        id: fingerprint,
        remoteAddress: remoteIP,
        remotePort: remotePort
      });
    });
  }

  var completeResponse = function(connection, toRender, messageCount){
    if(toRender === true){
      if(api.config.servers.web.metadataOptions.serverInformation){
        var stopTime = new Date().getTime();
        connection.response.serverInformation = {
          serverName:      api.config.general.serverName,
          apiVersion:      api.config.general.apiVersion,
          requestDuration: (stopTime - connection.connectedAt),
          currentTime:     stopTime
        };
      }

      if(api.config.servers.web.metadataOptions.requesterInformation){
        connection.response.requesterInformation = buildRequesterInformation(connection);
      }

      if(connection.response.error != null){
        if(shouldSendDocumentation(connection)){
          connection.response.documentation = api.documentation.documentation;
          delete connection.error;
          delete connection.response.error;
        } else if(api.config.servers.web.returnErrorCodes == true && connection.rawConnection.responseHttpCode == 200){
          if(connection.action == '{no action}' || String(connection.error).indexOf('is not a known action or that is not a valid apiVersion.') > 0){
            connection.rawConnection.responseHttpCode = 404;
          } else if(String(connection.error).indexOf('is a required parameter for this action') > 0){
            connection.rawConnection.responseHttpCode = 422;
          } else if(String(connection.error).indexOf('none of the required params for this action were provided') > 0){
            connection.rawConnection.responseHttpCode = 422;
          } else if('Error: ' +  api.config.general.serverErrorMessage == String(connection.response.error)){
            connection.rawConnection.responseHttpCode = 500;
          } else {
            connection.rawConnection.responseHttpCode = 400;
          }
        }
      }

      var stringResponse = JSON.stringify(connection.response, null, 2);
      if(connection.response.error == null &&
         connection.action != null &&
         connection.params.apiVersion != null &&
         api.actions.actions[connection.action][connection.params.apiVersion].matchExtensionMimeType === true
        ){
        connection.rawConnection.responseHeaders.push(['Content-Type', Mime.lookup(connection.extension)]);
      }
      if(connection.params.callback != null){
        connection.rawConnection.responseHeaders.push(['Content-Type', 'application/javascript']);
        stringResponse = connection.params.callback + '(' + stringResponse + ');';
      }

      server.sendMessage(connection, stringResponse);
    }
  }

  var connectionHasHeader = function(connection, match){
    for(var i in connection.rawConnection.responseHeaders){
      if(connection.rawConnection.responseHeaders[i][0].toLowerCase() === match.toLowerCase()){
        return true;
      }
    }
    return false;
  }

  var respondToOptions = function(connection){
    if(api.config.servers.web.httpHeaders['Access-Control-Allow-Methods'] == null && !connectionHasHeader(connection, 'Access-Control-Allow-Methods')){
      var methods = 'HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE';
      connection.rawConnection.responseHeaders.push(['Access-Control-Allow-Methods', methods]);
    }
    if(api.config.servers.web.httpHeaders['Access-Control-Allow-Origin'] == null && !connectionHasHeader(connection, 'Access-Control-Allow-Origin')){
      var origin = '*';
      connection.rawConnection.responseHeaders.push(['Access-Control-Allow-Origin', origin]);
    }
    server.sendMessage(connection, '');
  }

  var respondToTrace= function(connection){
    var data = buildRequesterInformation(connection);
    var stringResponse = JSON.stringify(data, null, 2);
    server.sendMessage(connection, stringResponse);
  }

  var determineRequestParams = function(connection, callback){
    var requestMode = api.config.servers.web.rootEndpointType; // api or public
    var pathParts = connection.rawConnection.parsedURL.pathname.split('/');
    var apiPathParts = connection.rawConnection.parsedURL.pathname.split('/');
    var filePathParts = connection.rawConnection.parsedURL.pathname.split('/');
    var extensionParts = connection.rawConnection.parsedURL.pathname.split('.');
    if (extensionParts.length > 1){
      connection.extension = extensionParts[(extensionParts.length - 1)];
    }
    filePathParts.shift();
    apiPathParts.shift();
    var i;
    if(pathParts.length > 0){
      if(pathParts[1] == api.config.servers.web.urlPathForActions){
        requestMode = 'api';
        apiPathParts.shift();
      } else if(pathParts[1] == api.config.servers.web.urlPathForFiles || connection.rawConnection.parsedURL.pathname.indexOf(api.config.servers.web.urlPathForFiles) === 0){
        requestMode = 'file';
        filePathParts.shift();
        i = 1;
        while(i < api.config.servers.web.urlPathForFiles.split('/').length - 1){
          filePathParts.shift();
          i++;
        }
      }
    }
    fillParamsFromWebRequest(connection, connection.rawConnection.parsedURL.query); // GET, PUT, and DELETE params
    if(requestMode == 'api'){
      var httpMethod = connection.rawConnection.req.method.toUpperCase();
      if(httpMethod == 'OPTIONS'){
        requestMode = 'options'
        callback(requestMode);
      }else if(httpMethod == 'GET' || httpMethod == 'HEAD'){
        api.routes.processRoute(connection);
        if(connection.params['action'] == null){ connection.params['action'] = apiPathParts[0]; }
        callback(requestMode);
      } else { // POST/DELETE/PUT params
        if(httpMethod == 'TRACE'){ requestMode = 'trace'; }
        if(connection.rawConnection.req.headers['content-type'] == null && connection.rawConnection.req.headers['Content-Type'] == null){
          // not a legal post; bad headers
          api.routes.processRoute(connection);
          if(connection.params['action'] == null){ connection.params['action'] = apiPathParts[0]; }
          callback(requestMode);
        } else {
          var form = new formidable.IncomingForm();
          for(i in api.config.servers.web.formOptions){
            form[i] = api.config.servers.web.formOptions[i];
          }
          form.parse(connection.rawConnection.req, function(err, fields, files) {
            if(err){
              server.log('error processing form: ' + String(err), 'error');
              connection.error = new Error('There was an error processing this form.');
            } else {
              fillParamsFromWebRequest(connection, files);
              fillParamsFromWebRequest(connection, fields);
            }
            api.routes.processRoute(connection);
            if(connection.params['action'] == null){ connection.params['action'] = apiPathParts[0]; }
            callback(requestMode);
          });
        }
      }
    } else {
      if(connection.params['file'] == null){
        connection.params['file'] = filePathParts.join('/');
        if(connection.params['file'] == '' || connection.params['file'][connection.params['file'].length - 1] == '/'){
          connection.params['file'] = connection.params['file'] + api.config.servers.web.directoryFileType;
        }
      }
      callback(requestMode);
    }
  }

  var fillParamsFromWebRequest = function(connection, varsHash){
    // helper for JSON posts
    var collapsedVarsHash = api.utils.collapseObjectToArray(varsHash);
    if(collapsedVarsHash !== false){
      varsHash = {payload: collapsedVarsHash} // post was an array, lets call it "payload"
    }else if(api.utils.hashLength(varsHash) == 1){
      var key = Object.keys(varsHash)[0];
    }

    for(var v in varsHash){
      connection.params[v] = varsHash[v];
    };
  }

  var shouldSendDocumentation = function(connection){
    if(connection.action != '{no action}'){ return false }
    if(connection.rawConnection.req.method.toUpperCase() != 'GET'){ return false }
    var params = api.utils.objClone(connection.params);
    delete params.action;
    delete params.limit;
    delete params.offset;
    delete params[''];
    return (api.utils.hashLength(params) === 0);
  }

  var buildRequesterInformation = function(connection){
    var requesterInformation = {
      id: connection.id,
      remoteIP: connection.remoteIP,
      receivedParams: {}
    };

    for(var p in connection.params){
      if(api.config.general.disableParamScrubbing === true || api.params.postVariables.indexOf(p) >= 0){
        requesterInformation.receivedParams[p] = connection.params[p];
      }
    }

    return requesterInformation;
  }

  var cleanHeaders = function(connection){
    var originalHeaders = connection.rawConnection.responseHeaders.reverse();
    var foundHeaders = [];
    var cleanedHeaders = [];
    for(var i in originalHeaders){
      var key = originalHeaders[i][0];
      var value = originalHeaders[i][1];
      if(foundHeaders.indexOf(key.toLowerCase()) >= 0 && key.toLowerCase().indexOf('set-cookie') < 0 ){
        // ignore, it's a duplicate
      } else if(connection.rawConnection.req.method.toUpperCase() == 'HEAD' && key == 'Transfer-Encoding'){
        // ignore, we can't send this header for HEAD requests
      } else {
        foundHeaders.push(key.toLowerCase());
        cleanedHeaders.push([key, value]);
      }
    }
    connection.rawConnection.responseHeaders = cleanedHeaders;
  }

  next(server);
}

/////////////////////////////////////////////////////////////////////
// exports
exports.web = web;
<|endoftext|>


<commit_before>'use strict';

const requireEsm = require('esm')(module);
function getFeatures() {
  const { DEFAULT_FEATURES: features } = requireEsm(
    '@ember-data/canary-features/addon/default-features.js'
  );

  const FEATURE_OVERRIDES = process.env.EMBER_DATA_FEATURE_OVERRIDE;
  if (FEATURE_OVERRIDES === 'ENABLE_ALL_OPTIONAL') {
    // enable all features with a current value of `null`
    for (let feature in features) {
      let featureValue = features[feature];

      if (featureValue === null) {
        features[feature] = true;
      }
    }
  } else if (FEATURE_OVERRIDES) {
    // enable only the specific features listed in the environment
    // variable (comma separated)
    const forcedFeatures = FEATURE_OVERRIDES.split(',');
    for (let i = 0; i < forcedFeatures.length; i++) {
      let featureName = forcedFeatures[i];

      features[featureName] = true;
    }
  }

  return features;
}

module.exports = getFeatures();
<commit_msg>Update packages/-build-infra/src/features.js<commit_after>'use strict';

const requireEsm = require('esm')(module);
function getFeatures() {
  const { default: features } = requireEsm(
    '@ember-data/canary-features/addon/default-features.js'
  );

  const FEATURE_OVERRIDES = process.env.EMBER_DATA_FEATURE_OVERRIDE;
  if (FEATURE_OVERRIDES === 'ENABLE_ALL_OPTIONAL') {
    // enable all features with a current value of `null`
    for (let feature in features) {
      let featureValue = features[feature];

      if (featureValue === null) {
        features[feature] = true;
      }
    }
  } else if (FEATURE_OVERRIDES) {
    // enable only the specific features listed in the environment
    // variable (comma separated)
    const forcedFeatures = FEATURE_OVERRIDES.split(',');
    for (let i = 0; i < forcedFeatures.length; i++) {
      let featureName = forcedFeatures[i];

      features[featureName] = true;
    }
  }

  return features;
}

module.exports = getFeatures();
<|endoftext|>


<commit_before>"use strict";

/*  ------------------------------------------------------------------------ */

const ccxt        = require ('../../ccxt.js')
    , asTable     = require ('as-table') // .configure ({ print: require ('string.ify').noPretty })
    , log         = require ('ololog').noLocate
    , ansi        = require ('ansicolor').nice

;(async function test () {

    let total = 0
    let missing = 0
    let implemented = 0
    let emulated = 0

    log (asTable (ccxt.exchanges.map (id => new ccxt[id]()).map (exchange => {

        let result = {};

        [
            'publicAPI',
            'privateAPI',
            'CORS',
            'fetchTicker',
            'fetchTickers',
            'fetchOrderBook',
            'fetchTrades',
            'fetchOHLCV',
            'fetchBalance',
            'createOrder',
            'createMarketOrder',
            'createLimitOrder',
            'editOrder',
            'cancelOrder',
            'fetchOrder',
            'fetchOrders',
            'fetchOpenOrders',
            'fetchClosedOrders',
            'fetchMyTrades',
            'fetchCurrencies',
            'fetchDepositAddress',
            'createDepositAddress',
            'withdraw',

        ].forEach (key => {

            total += 1

            let capability = exchange.has[key].toString ()

            if (!exchange.has[key]) {
                capability = exchange.id.red.dim
                missing += 1
            } else if (exchange.has[key] === 'emulated') {
                capability = exchange.id.yellow
                emulated += 1
            } else {
                capability = exchange.id.green
                implemented += 1
            }

            result[key] = capability
        })

        return result
    })))

    log ('Methods:',
        implemented.toString ().green, 'implemented,',
        emulated.toString ().yellow, 'emulated,',
        missing.toString ().red, 'missing,',
        total.toString (), 'total')

}) ()
<commit_msg>more properties to examples/js/exchange-capabilities<commit_after>"use strict";

/*  ------------------------------------------------------------------------ */

const ccxt        = require ('../../ccxt.js')
    , asTable     = require ('as-table') // .configure ({ print: require ('string.ify').noPretty })
    , log         = require ('ololog').noLocate
    , ansi        = require ('ansicolor').nice

;(async function test () {

    let total = 0
    let missing = 0
    let implemented = 0
    let emulated = 0

    log (asTable (ccxt.exchanges.map (id => new ccxt[id]()).map (exchange => {

        let result = {};

        [
            'publicAPI',
            'privateAPI',
            'CORS',
            'fetchTicker',
            'fetchTickers',
            'fetchOrderBook',
            'fetchTrades',
            'fetchOHLCV',
            'fetchBalance',
            'createOrder',
            'createMarketOrder',
            'createLimitOrder',
            'editOrder',
            'cancelOrder',
            'cancelOrders',
            'cancelAllOrders',
            'fetchOrder',
            'fetchOrders',
            'fetchOpenOrders',
            'fetchClosedOrders',
            'fetchMyTrades',
            'fetchCurrencies',
            'fetchDepositAddress',
            'createDepositAddress',
            'fetchTransactions',
            'fetchDeposits',
            'fetchWithdrawals',
            'withdraw',
            'fetchLedger',
            'fetchFundingFees',
            'fetchTradingFees',
            'fetchTradingLimits',

        ].forEach (key => {

            total += 1

            let capability = exchange.has[key].toString ()

            if (!exchange.has[key]) {
                capability = exchange.id.red.dim
                missing += 1
            } else if (exchange.has[key] === 'emulated') {
                capability = exchange.id.yellow
                emulated += 1
            } else {
                capability = exchange.id.green
                implemented += 1
            }

            result[key] = capability
        })

        return result
    })))

    log ('Methods:',
        implemented.toString ().green, 'implemented,',
        emulated.toString ().yellow, 'emulated,',
        missing.toString ().red, 'missing,',
        total.toString (), 'total')

}) ()
<|endoftext|>


<commit_before>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

// This is a Hello World function which writes to the database.
exports.helloWorld = functions.database.ref('/hello/{name}').onWrite((() => {var _ref = (0, _asyncToGenerator3.default)(function* (event) {
    yield event.data.ref.set(`Hello ${event.params.name}`);
    console.log('Counter updated.');
    return;
  });return function (_x) {return _ref.apply(this, arguments);};})());<commit_msg>Remove transpiled<commit_after><|endoftext|>


<commit_before>var PositionSticky = {

  POS_SCHEME_STATIC:   100,
  POS_SCHEME_FIXED:    200,
  POS_SCHEME_ABSOLUTE: 300,

  create: function(element, options) {
    if (typeof options === 'undefined') {
      options = {};
    }
    return Object.create(PositionSticky).init(element, options);
  },

  init: function(element, options) {
    this.constructor = PositionSticky;
    this.window = window;
    this.element = element;
    this.container = element.parentNode;
    this.posScheme = PositionSticky.POS_SCHEME_STATIC;
    this.isTicking = false;
    this.threshold = null;
    this.options = options;
    this.boundingBoxHeight = null;
    this.latestKnownScrollY = this.window.pageYOffset;

    this.validateContainerPosScheme();
    this.setOffsetTop();
    this.setOffsetBottom();
    this.calcThreshold();
    this.setElementWidth();
    this.setBoundingBoxHeight();
    this.createPlaceholder();
    this.subscribeToWindowScroll();

    return this;
  },

  validateContainerPosScheme: function() {
    var containerPosScheme = this.container.style.position;
    if (containerPosScheme != 'relative' && containerPosScheme != 'absolute') {
      this.container.style.position = 'relative';
    }
  },

  setOffsetTop: function() {
    if (typeof this.options.offsetTop === 'number' && this.options.offsetTop >= 0) {
      this.offsetTop = this.options.offsetTop;
    } else {
      var topBorderWidth = parseInt(this.window.getComputedStyle(this.container).borderTopWidth, 10);
      var topPadding = parseInt(this.window.getComputedStyle(this.container).paddingTop, 10);
      this.offsetTop = topBorderWidth + topPadding;
    }
  },

  setOffsetBottom: function() {
    var bottomBorderWidth = parseInt(this.window.getComputedStyle(this.container).borderBottomWidth, 10);
    var bottomPadding = parseInt(this.window.getComputedStyle(this.container).paddingBottom, 10);
    this.offsetBottom = bottomBorderWidth + bottomPadding;
  },

  calcThreshold: function() {
    this.threshold = this.getElementDistanceFromDocumentTop() - this.offsetTop;
  },

  setElementWidth: function() {
    var width = this.window.getComputedStyle(this.element).width;
    this.element.style.width = width;
  },

  setBoundingBoxHeight: function(updatePlaceholder) {
    this.boundingBoxHeight = this.element.getBoundingClientRect().height;
    if (updatePlaceholder === true) {
      this.placeholder.style.height = this.boundingBoxHeight + 'px';
    }
  },

  createPlaceholder: function() {
    var placeholder = document.createElement('DIV');

    var width   = this.element.getBoundingClientRect().width + 'px';
    var height  = this.boundingBoxHeight + 'px';
    var margin  = this.window.getComputedStyle(this.element).margin;
    var float   = this.window.getComputedStyle(this.element).float; // TODO: Doesn't work on Firefox

    placeholder.style.display = 'none';
    placeholder.style.width   = width;
    placeholder.style.height  = height;
    placeholder.style.margin  = margin;
    placeholder.style.float   = float;

    this.container.insertBefore(placeholder, this.element);
    this.placeholder = placeholder;
  },

  subscribeToWindowScroll: function() {
    this.window.addEventListener('scroll', this.onScroll.bind(this));
  },

  /**
   * TODO: Don't run update when container is not visible
   */
  onScroll: function() {
    if (!this.isTicking) {
      this.latestKnownScrollY = this.window.pageYOffset;
      this.window.requestAnimationFrame(this.update.bind(this));
      this.isTicking = true;
    }
  },

  isStatic: function() {
    return this.posScheme === PositionSticky.POS_SCHEME_STATIC;
  },

  makeStatic: function() {
    this.element.style.position = 'static';
    this.placeholder.style.display = 'none';
    this.posScheme = PositionSticky.POS_SCHEME_STATIC;
  },

  isFixed: function() {
    return this.posScheme === PositionSticky.POS_SCHEME_FIXED;
  },

  makeFixed: function() {
    this.element.style.bottom = null;
    this.element.style.position = 'fixed';
    this.element.style.top = this.offsetTop + 'px';
    this.placeholder.style.display = 'block';
    this.posScheme = PositionSticky.POS_SCHEME_FIXED;
  },

  isAbsolute: function() {
    return this.posScheme === PositionSticky.POS_SCHEME_ABSOLUTE;
  },

  makeAbsolute: function() {
    this.element.style.top = null;
    this.element.style.position = 'absolute';
    this.element.style.bottom = this.offsetBottom + 'px';
    this.placeholder.style.display = 'block';
    this.posScheme = PositionSticky.POS_SCHEME_ABSOLUTE;
  },

  update: function() {
    this.isTicking = false;

    if (this.isBelowThreshold()) {
      if (!this.isStatic()) {
        this.makeStatic();
      }
    } else if (this.canStickyFitInContainer()) {
      if (!this.isFixed()) {
        this.makeFixed();
      }
    } else {
      if (!this.isAbsolute()) {
        this.makeAbsolute();
      }
    }
  },

  isBelowThreshold: function() {
    if (this.latestKnownScrollY < this.threshold) {
      return true;
    }
    return false;
  },

  canStickyFitInContainer: function() {
    return this.getAvailableSpaceInContainer() >= this.boundingBoxHeight;
  },

  getAvailableSpaceInContainer: function() {
    return this.container.getBoundingClientRect().bottom - this.offsetBottom - this.offsetTop;
  },

  getElementDistanceFromDocumentTop: function() {
    var element = (this.isStatic() ? this.element : this.placeholder);
    var totalOffsetTop = this.latestKnownScrollY + element.getBoundingClientRect().top;
    return totalOffsetTop;
  },

  refresh: function() {
    this.calcThreshold();
    this.setBoundingBoxHeight(true);
  }

};<commit_msg>Added some documentation<commit_after>/**
 * @namespace PositionSticky
 * @author Ahmet Katrancı <ahmet@katranci.co.uk>
 */
var PositionSticky = {

  /**
   * @constant
   */
  POS_SCHEME_STATIC:   100,

  /**
   * @constant
   */
  POS_SCHEME_FIXED:    200,

  /**
   * @constant
   */
  POS_SCHEME_ABSOLUTE: 300,

  /**
   * Creates an instance of PositionSticky
   *
   * @param element
   * @param options
   * @returns {PositionSticky}
   * @static
   */
  create: function(element, options) {
    if (typeof options === 'undefined') {
      options = {};
    }
    return Object.create(PositionSticky).init(element, options);
  },

  /**
   * Constructor method
   *
   * @param element {HTMLElement}
   * @param options {Object}
   * @returns {PositionSticky}
   * @instance
   */
  init: function(element, options) {
    this.constructor = PositionSticky;
    this.window = window;
    this.element = element;
    this.container = element.parentNode;
    this.posScheme = PositionSticky.POS_SCHEME_STATIC;
    this.isTicking = false;
    this.threshold = null;
    this.options = options;
    this.boundingBoxHeight = null;
    this.latestKnownScrollY = this.window.pageYOffset;

    this.validateContainerPosScheme();
    this.setOffsetTop();
    this.setOffsetBottom();
    this.calcThreshold();
    this.setElementWidth();
    this.setBoundingBoxHeight();
    this.createPlaceholder();
    this.subscribeToWindowScroll();

    return this;
  },

  /**
   * Ensures that the container's position is either 'relative' or 'absolute'
   * so that when the sticky element is positioned absolutely it is positioned within its container
   *
   * @instance
   */
  validateContainerPosScheme: function() {
    var containerPosScheme = this.container.style.position;
    if (containerPosScheme != 'relative' && containerPosScheme != 'absolute') {
      this.container.style.position = 'relative';
    }
  },

  /**
   * Sets the distance that the sticky element will have from the top of viewport
   * when it becomes sticky
   *
   * @instance
   */
  setOffsetTop: function() {
    if (typeof this.options.offsetTop === 'number' && this.options.offsetTop >= 0) {
      this.offsetTop = this.options.offsetTop;
    } else {
      var topBorderWidth = parseInt(this.window.getComputedStyle(this.container).borderTopWidth, 10);
      var topPadding = parseInt(this.window.getComputedStyle(this.container).paddingTop, 10);
      this.offsetTop = topBorderWidth + topPadding;
    }
  },

  /**
   * Sets the distance that the sticky element will have from the bottom of its container
   * when it is positioned absolutely
   *
   * @instance
   */
  setOffsetBottom: function() {
    var bottomBorderWidth = parseInt(this.window.getComputedStyle(this.container).borderBottomWidth, 10);
    var bottomPadding = parseInt(this.window.getComputedStyle(this.container).paddingBottom, 10);
    this.offsetBottom = bottomBorderWidth + bottomPadding;
  },

  /**
   * Calculates the point where the sticky behaviour should start
   *
   * @instance
   */
  calcThreshold: function() {
    this.threshold = this.getElementDistanceFromDocumentTop() - this.offsetTop;
  },

  /**
   * Applies element's computed width to its inline styling so that when the element
   * is positioned absolutely or fixed it doesn't lose its shape
   *
   * @instance
   */
  setElementWidth: function() {
    var width = this.window.getComputedStyle(this.element).width;
    this.element.style.width = width;
  },

  /**
   * Saves element's bounding box height to an instance property so that it is not
   * calculated on every #update. When updatePlaceholder boolean is true, it also
   * updates the placeholder's height.
   *
   * @param updatePlaceholder {boolean}
   * @instance
   */
  setBoundingBoxHeight: function(updatePlaceholder) {
    this.boundingBoxHeight = this.element.getBoundingClientRect().height;
    if (updatePlaceholder === true) {
      this.placeholder.style.height = this.boundingBoxHeight + 'px';
    }
  },

  /**
   * Creates the placeholder that will be used in place of the element
   * when the element is positioned absolutely or fixed
   *
   * @instance
   */
  createPlaceholder: function() {
    var placeholder = document.createElement('DIV');

    var width   = this.element.getBoundingClientRect().width + 'px';
    var height  = this.boundingBoxHeight + 'px';
    var margin  = this.window.getComputedStyle(this.element).margin;
    var float   = this.window.getComputedStyle(this.element).float; // TODO: Doesn't work on Firefox

    placeholder.style.display = 'none';
    placeholder.style.width   = width;
    placeholder.style.height  = height;
    placeholder.style.margin  = margin;
    placeholder.style.float   = float;

    this.container.insertBefore(placeholder, this.element);
    this.placeholder = placeholder;
  },

  /**
   * Attaches #onScroll method to Window.onscroll event
   *
   * @instance
   */
  subscribeToWindowScroll: function() {
    this.window.addEventListener('scroll', this.onScroll.bind(this));
  },

  /**
   * Debounces the scroll event
   *
   * @see [Debouncing Scroll Events]{@link http://www.html5rocks.com/en/tutorials/speed/animations/#debouncing-scroll-events}
   * @instance
   *
   * @todo Don't run update when container is not visible
   */
  onScroll: function() {
    if (!this.isTicking) {
      this.latestKnownScrollY = this.window.pageYOffset;
      this.window.requestAnimationFrame(this.update.bind(this));
      this.isTicking = true;
    }
  },

  /**
   * @returns {boolean}
   * @instance
   */
  isStatic: function() {
    return this.posScheme === PositionSticky.POS_SCHEME_STATIC;
  },

  /**
   * @instance
   */
  makeStatic: function() {
    this.element.style.position = 'static';
    this.placeholder.style.display = 'none';
    this.posScheme = PositionSticky.POS_SCHEME_STATIC;
  },

  /**
   * @returns {boolean}
   * @instance
   */
  isFixed: function() {
    return this.posScheme === PositionSticky.POS_SCHEME_FIXED;
  },

  /**
   * @instance
   */
  makeFixed: function() {
    this.element.style.bottom = null;
    this.element.style.position = 'fixed';
    this.element.style.top = this.offsetTop + 'px';
    this.placeholder.style.display = 'block';
    this.posScheme = PositionSticky.POS_SCHEME_FIXED;
  },

  /**
   * @returns {boolean}
   * @instance
   */
  isAbsolute: function() {
    return this.posScheme === PositionSticky.POS_SCHEME_ABSOLUTE;
  },

  /**
   * @instance
   */
  makeAbsolute: function() {
    this.element.style.top = null;
    this.element.style.position = 'absolute';
    this.element.style.bottom = this.offsetBottom + 'px';
    this.placeholder.style.display = 'block';
    this.posScheme = PositionSticky.POS_SCHEME_ABSOLUTE;
  },

  /**
   * This is the main method that runs on every animation frame during scroll.
   * It starts with checking whether the element is within the static range.
   * If not, it checks whether the element is within the fixed range.
   * Otherwise, it positions the element absolutely.
   *
   * @instance
   */
  update: function() {
    this.isTicking = false;

    if (this.isBelowThreshold()) {
      if (!this.isStatic()) {
        this.makeStatic();
      }
    } else if (this.canStickyFitInContainer()) {
      if (!this.isFixed()) {
        this.makeFixed();
      }
    } else {
      if (!this.isAbsolute()) {
        this.makeAbsolute();
      }
    }
  },

  /**
   * Returns true when the page hasn't been scrolled to the threshold point yet.
   * Otherwise, returns false.
   *
   * @returns {boolean}
   * @instance
   */
  isBelowThreshold: function() {
    if (this.latestKnownScrollY < this.threshold) {
      return true;
    }
    return false;
  },

  /**
   * Checks whether the element can fit inside the visible portion of the container or not
   *
   * @returns {boolean}
   * @instance
   */
  canStickyFitInContainer: function() {
    return this.getAvailableSpaceInContainer() >= this.boundingBoxHeight;
  },

  /**
   * Calculates the height of the visible portion of the container
   * that can be used to fit the sticky element
   *
   * @returns {number}
   * @instance
   */
  getAvailableSpaceInContainer: function() {
    return this.container.getBoundingClientRect().bottom - this.offsetBottom - this.offsetTop;
  },

  /**
   * Calculates element's total offset from the document top.
   * It uses placeholder if page is scrolled and element became sticky already.
   *
   * @returns {number}
   * @instance
   */
  getElementDistanceFromDocumentTop: function() {
    var element = (this.isStatic() ? this.element : this.placeholder);
    var totalOffsetTop = this.latestKnownScrollY + element.getBoundingClientRect().top;
    return totalOffsetTop;
  },

  /**
   * Re-measures the cached positions/dimensions that are used during scroll
   *
   * @instance
   */
  refresh: function() {
    this.calcThreshold();
    this.setBoundingBoxHeight(true);
  }

};<|endoftext|>


<commit_before>var request = require('request')

var insults = {

  var url = 'https://www.foaas.com/thanks/nick'
  var options = {
    url: url,
    method: 'GET'
  }

  function callback(error, response, body) {
      if (!error && response.statusCode == 200) {
          // Print out the response body
          var result = JSON.parse(body)
          var message = result.message
          var subtitle = result.subtitle
          console.log('message:' + message)
          console.log('subtitle:' + subtitle)
      }
  }

  var regex = '.*'
  var location = 'ambient'

  function(slapp) {
    slapp.
      message(regex,location, (msg) => {
        request(options, callback)
      }
  }
}

module.exports = insults(slapp)
<commit_msg>teach me to node<commit_after>var insults = {
  function(slapp) {
    var request = require('request')
    var url = 'https://www.foaas.com/thanks/nick'
    var options = {
      url: url,
      method: 'GET'
    }

    function callback(error, response, body) {
        if (!error && response.statusCode == 200) {
            // Print out the response body
            var result = JSON.parse(body)
            var message = result.message
            var subtitle = result.subtitle
            console.log('message:' + message)
            console.log('subtitle:' + subtitle)
        }
    }

    var regex = '.*'
    var location = 'ambient'

    function(slapp) {
      slapp.
        message(regex,location, (msg) => {
          request(options, callback)
        }
    }
  }
}

module.exports = insults
<|endoftext|>


<commit_before>var files = {
	"light": [
		"light_directlight",
		"light_pointlight",
		"light_spotlight"
	],
	"geometry": [
		"geometry_geometries",
		"geometry_loader_assimp2json"
	],
	"material": [
		"material_materials",
		"material_texture_normalmap",
		"material_texture_mipmap",
		"material_cubetexture_skybox",
		"material_cubetexture_envmap",
		"material_cubetexture_realtimeenvmap",
		"material_transparent"
	],
	"camera": [
		"camera_cameras",
		"camera_controller_hover"
	],
	"scene": [
		"scene_fog"
	],
	"math": [
		"math_frustumtest"
	],
	"sprite": [
		"sprite_sprites"
	],
	"points": [
		"points_sprites"
	],
	"canvas2d": [
		"canvas2d_canvas2d"
	],
	"raycast": [
		"raycast_raycaster"
	],
	"webvr": [
		"webvr_car"
	],
	"physics": [
		"physics_cannon"
	],
	"help": [
		"help_performance"
	],
	"particle": [
		"particle_particle"
	]
};
<commit_msg>add curve demo<commit_after>var files = {
	"light": [
		"light_directlight",
		"light_pointlight",
		"light_spotlight"
	],
	"geometry": [
		"geometry_geometries",
		"geometry_loader_assimp2json"
	],
	"material": [
		"material_materials",
		"material_texture_normalmap",
		"material_texture_mipmap",
		"material_cubetexture_skybox",
		"material_cubetexture_envmap",
		"material_cubetexture_realtimeenvmap",
		"material_transparent"
	],
	"camera": [
		"camera_cameras",
		"camera_controller_hover"
	],
	"scene": [
		"scene_fog"
	],
	"math": [
		"math_frustumtest",
		"math_curve"
	],
	"sprite": [
		"sprite_sprites"
	],
	"points": [
		"points_sprites"
	],
	"canvas2d": [
		"canvas2d_canvas2d"
	],
	"raycast": [
		"raycast_raycaster"
	],
	"webvr": [
		"webvr_car"
	],
	"physics": [
		"physics_cannon"
	],
	"help": [
		"help_performance"
	],
	"particle": [
		"particle_particle"
	]
};
<|endoftext|>


<commit_before>if (mb == null || typeof(mb) != "object") { var mb = new Object(); }

mb.SERVER = '127.0.0.1/my-basket/';

/**@constructor*/
mb.Contact = function() {
	//Database ID
	this.uid = -1;

	//Profile of the contact
	this.profileInfo = new MockUserProfile();

	//Database connection
	this.db = new MockDatabase();
};

//TODO: implement create function for inserting into ContactLists
mb.Contact.prototype.create = function() {

};

//TODO: implement in case of user name change
mb.Contact.prototype.update = function() {

};

mb.Contact.prototype.toString = function() {
	return this.profileInfo.toString();
};

<commit_msg>Small changes to contact format<commit_after>if (mb == null || typeof(mb) != "object") { var mb = new Object(); }

mb.SERVER = '127.0.0.1/my-basket/';

/**@constructor*/
mb.Contact = function() {
	//Database ID
	this.uid = -1;

	//Username of the contact
	this.username = 'Peter Placeholder';

	//Database connection
	this.db = new MockDatabase();
};

//TODO: implement create function for inserting into ContactLists
mb.Contact.prototype.create = function() {

};

//TODO: implement in case of username change
mb.Contact.prototype.update = function() {

};

mb.Contact.prototype.toString = function() {
	return this.profileInfo.toString();
};

<|endoftext|>


<commit_before>import * as d3 from 'd3';
import Timer from './timer';

export default class {
	constructor(id, svg, duration, width, thickness){
		this.id = id;
		this.svg = svg;
		this.width = width;
		this.radius = this.width / 2;
		this.thickness = thickness;

		// Store the displayed angles in _current.
		// Then, interpolate from _current to the new angles.
		// During the transition, _current is updated in-place by d3.interpolate.
		function arcTween(a) {
			var i = d3.interpolate(this._current, a);
			this._current = i(0);
			return function(t) {
				return arc(i(t));
			};
		}

		this.draw = function(){
			console.log('draw');
			g.datum(this.timerData);
			path.data(pie); // compute the new angles
		    path.transition().duration(1000).attrTween("d", arcTween); // redraw the arcs
		}
		this.start = function(){
			this.timer.start();
		}

		this.timer = new Timer(duration, 1001, ::this.draw, ::this.draw);

		const data = this.timerData;
		const colorScale = d3.scaleLinear().domain([0, data.length - 1]).range([.8, .2]);
		const color = function(index){
			return d3.interpolateInferno(colorScale(index));
		};

		const pie = d3.pie().sort(null);

		const arc = d3.arc()
		.innerRadius(this.radius - thickness)
		.outerRadius(this.radius);

		const g = this.svg
		.append("g")
		.attr("transform", "translate(" + this.width / 2 + "," + this.width / 2 + ")");

		let path = g.datum(data).selectAll("path")
		.data(pie)
		.enter().append("path")
		.attr("fill", function(d, i) { return color(i); })
		.attr("d", arc)
	    .each(function(d) { this._current = d; }); // store the initial angles
	}

	get elapsed(){
		if (!this.running){
			return 0;
		}
		return Date.now() - this.startedAt;
	}

	get timerData(){
		return this.constructor.generateTimerData(this.timer.duration, this.timer.elapsed);
	}

	/**
	 * For a duration of n seconds, generates an array with n+1 numbers initialized to 0.
	 * Number of elapsed milliseconds will be reflected with appropriate number of array indices
	 * set to 1000, with last item in array holding remaining duration.
	 */
	static generateTimerData(duration, elapsed){
		console.log('duration, elapsed', duration, elapsed);
		const numIndices = Math.floor(duration / 1000);
		const numIndicesElapsed = Math.ceil(elapsed / 1000);
		let result = Array(numIndices + 1).fill(0);
		for(let i = 0; i < numIndicesElapsed; i++){
			result[i] = 1000;
		}
		result[result.length - 1] = (numIndices - numIndicesElapsed) * 1000;
		console.log('numIndices', numIndices, 'numIndicesElapsed', numIndicesElapsed);
		console.log(result);
		return result;
	}
}<commit_msg>moving to manual animations/easing rather than d3.transition. Currently exhibiting a "zip around" donut animation<commit_after>import * as d3 from 'd3';
import Timer from './timer';

export default class {
	constructor(id, svg, duration, width, thickness){
		this.id = id;
		this.svg = svg;
		this.width = width;
		this.radius = this.width / 2;
		this.thickness = thickness;

		const pie = d3.pie().sort(null);

		const arc = d3.arc()
		.innerRadius(this.radius - thickness)
		.outerRadius(this.radius);

		this.draw = function(){
			g.datum(this.timerData);
			path.data(pie)
				.attr("d", arcEased);
		}
		this.start = function(){
			this.timer.start();
		}

		this.timer = new Timer(duration, ::this.draw, ::this.draw);

		const arcEased = function(d, i){
			var interpolate = d3.interpolate(this._current, d);
			return arc(interpolate(d3.easeCubic(oneSecondScaleIn(d.value))));
		}

		const data = this.timerData;
		const oneSecondScaleIn = d3.scaleLinear().domain([0, 1000]).range([0, 1]);
		const colorScale = d3.scaleLinear().domain([0, data.length - 1]).range([.8, .2]);
		const color = function(index){
			return d3.interpolateInferno(colorScale(index));
		};

		const g = this.svg
		.append("g")
		.attr("transform", "translate(" + this.width / 2 + "," + this.width / 2 + ")");

		const path = g.datum(data).selectAll("path")
		.data(pie)
		.enter().append("path")
		.attr("fill", function(d, i) { return color(i); })
		.attr("d", arc)
	    .each(function(d) { this._current = d; }); // store the initial angles
	}

	get elapsed(){
		if (!this.running){
			return 0;
		}
		return Date.now() - this.startedAt;
	}

	get timerData(){
		return this.constructor.generateTimerData(this.timer.duration, this.timer.elapsed);
	}

	/**
	 * For a duration of n seconds, generates an array with n+1 numbers initialized to 0.
	 * Number of elapsed milliseconds will be reflected with appropriate number of array indices
	 * set to 1000, with last item in array holding remaining duration.
	 */
	static generateTimerData(duration, elapsed){
		console.log('duration, elapsed', duration, elapsed);
		const numIndices = Math.floor(duration / 1000);
		const numIndicesCompleted = Math.floor(elapsed / 1000);
		let result = Array(numIndices + 1).fill(0);
		for(let i = 0; i < numIndicesCompleted; i++){
			result[i] = 1000;
		}
		result[numIndicesCompleted] = elapsed % 1000;
		result[result.length - 1] = duration - elapsed; //(numIndices - numIndicesElapsed) * 1000;
		console.log('numIndices', numIndices, 'numIndicesCompleted', numIndicesCompleted);
		console.log(result);
		return result;
	}
}<|endoftext|>


<commit_before>'use strict';

// This alternative WebpackDevServer combines the functionality of:
// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js

// It only supports their simplest configuration (hot updates on same server).
// It makes some opinionated choices on top, like adding a syntax error overlay
// that looks similar to our console output. The error overlay is inspired by:
// https://github.com/glenjamin/webpack-hot-middleware

var SockJS = require('sockjs-client');
var stripAnsi = require('strip-ansi');
var url = require('url');
var launchEditorEndpoint = require('react-dev-utils/launchEditorEndpoint');
var devServerMajorVersion = require('./devServerMajor');
var formatWebpackMessages = require('./formatWebpackMessages');
var ErrorOverlay = require('react-error-overlay');

var socketUrl = createSocketUrl();
var parsedSocketUrl = url.parse(socketUrl);

var createSocketUrl;
if (devServerMajorVersion > 3) {
  // The path changed with v4
  createSocketUrl = require('webpack-dev-server/client/utils/createSocketURL');
} else {
  createSocketUrl = require('webpack-dev-server/client/utils/createSocketUrl');
}
console.error('hotDevClient', { devServerMajorVersion });

ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) {
  // Keep this sync with errorOverlayMiddleware.js
  fetch(
    url.format({
      protocol: parsedSocketUrl.protocol,
      hostname: parsedSocketUrl.hostname,
      port: parsedSocketUrl.port,
      pathname: launchEditorEndpoint,
      search:
        '?fileName=' +
        window.encodeURIComponent(errorLocation.fileName) +
        '&lineNumber=' +
        window.encodeURIComponent(errorLocation.lineNumber || 1) +
        '&colNumber=' +
        window.encodeURIComponent(errorLocation.colNumber || 1),
    }),
    { mode: 'no-cors' }
  );
});

// We need to keep track of if there has been a runtime error.
// Essentially, we cannot guarantee application state was not corrupted by the
// runtime error. To prevent confusing behavior, we forcibly reload the entire
// application. This is handled below when we are notified of a compile (code
// change).
// See https://github.com/facebookincubator/create-react-app/issues/3096
var hadRuntimeError = false;
ErrorOverlay.startReportingRuntimeErrors({
  onError: function() {
    hadRuntimeError = true;
  },
  filename: process.env.REACT_BUNDLE_PATH || '/static/js/bundle.js',
});

if (module.hot && typeof module.hot.dispose === 'function') {
  module.hot.dispose(function() {
    // TODO: why do we need this?
    ErrorOverlay.stopReportingRuntimeErrors();
  });
}

// Connect to WebpackDevServer via a socket.
var connection = new SockJS(socketUrl);

// Unlike WebpackDevServer client, we won't try to reconnect
// to avoid spamming the console. Disconnect usually happens
// when developer stops the server.
connection.onclose = function() {
  if (typeof console !== 'undefined' && typeof console.info === 'function') {
    console.info(
      'The development server has disconnected.\nRefresh the page if necessary.'
    );
  }
};

// Remember some state related to hot module replacement.
var isFirstCompilation = true;
var mostRecentCompilationHash = null;
var hasCompileErrors = false;

function clearOutdatedErrors() {
  // Clean up outdated compile errors, if any.
  if (typeof console !== 'undefined' && typeof console.clear === 'function') {
    if (hasCompileErrors) {
      console.clear();
    }
  }
}

// Successful compilation.
function handleSuccess() {
  clearOutdatedErrors();

  var isHotUpdate = !isFirstCompilation;
  isFirstCompilation = false;
  hasCompileErrors = false;

  // Attempt to apply hot updates or reload.
  if (isHotUpdate) {
    tryApplyUpdates(function onHotUpdateSuccess() {
      // Only dismiss it when we're sure it's a hot update.
      // Otherwise it would flicker right before the reload.
      tryDismissErrorOverlay();
    });
  }
}

// Compilation with warnings (e.g. ESLint).
function handleWarnings(warnings) {
  clearOutdatedErrors();

  var isHotUpdate = !isFirstCompilation;
  isFirstCompilation = false;
  hasCompileErrors = false;

  function printWarnings() {
    // Print warnings to the console.
    var formatted = formatWebpackMessages({
      warnings: warnings,
      errors: [],
    });

    if (typeof console !== 'undefined' && typeof console.warn === 'function') {
      for (var i = 0; i < formatted.warnings.length; i++) {
        if (i === 5) {
          console.warn(
            'There were more warnings in other files.\n' +
              'You can find a complete log in the terminal.'
          );
          break;
        }
        console.warn(stripAnsi(formatted.warnings[i]));
      }
    }
  }

  // Attempt to apply hot updates or reload.
  if (isHotUpdate) {
    tryApplyUpdates(function onSuccessfulHotUpdate() {
      // Only print warnings if we aren't refreshing the page.
      // Otherwise they'll disappear right away anyway.
      printWarnings();
      // Only dismiss it when we're sure it's a hot update.
      // Otherwise it would flicker right before the reload.
      tryDismissErrorOverlay();
    });
  } else {
    // Print initial warnings immediately.
    printWarnings();
  }
}

// Compilation with errors (e.g. syntax error or missing modules).
function handleErrors(errors) {
  clearOutdatedErrors();

  isFirstCompilation = false;
  hasCompileErrors = true;

  // "Massage" webpack messages.
  var formatted = formatWebpackMessages({
    errors: errors,
    warnings: [],
  });

  // Only show the first error.
  ErrorOverlay.reportBuildError(formatted.errors[0]);

  // Also log them to the console.
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    for (var i = 0; i < formatted.errors.length; i++) {
      console.error(stripAnsi(formatted.errors[i]));
    }
  }

  // Do not attempt to reload now.
  // We will reload on next success instead.
}

function tryDismissErrorOverlay() {
  if (!hasCompileErrors) {
    ErrorOverlay.dismissBuildError();
  }
}

// There is a newer version of the code available.
function handleAvailableHash(hash) {
  // Update last known compilation hash.
  mostRecentCompilationHash = hash;
}

// Handle messages from the server.
connection.onmessage = function(e) {
  var message = JSON.parse(e.data);
  switch (message.type) {
    case 'hash':
      handleAvailableHash(message.data);
      break;
    case 'still-ok':
    case 'ok':
      handleSuccess();
      break;
    case 'content-changed':
      // Triggered when a file from `contentBase` changed.
      window.location.reload();
      break;
    case 'warnings':
      handleWarnings(message.data);
      break;
    case 'errors':
      handleErrors(message.data);
      break;
    default:
    // Do nothing.
  }
};

// Is there a newer version of this code available?
function isUpdateAvailable() {
  /* globals __webpack_hash__ */
  // __webpack_hash__ is the hash of the current compilation.
  // It's a global variable injected by Webpack.
  return mostRecentCompilationHash !== __webpack_hash__;
}

// Webpack disallows updates in other states.
function canApplyUpdates() {
  return module.hot.status() === 'idle';
}

// Attempt to update code on the fly, fall back to a hard reload.
function tryApplyUpdates(onHotUpdateSuccess) {
  if (!module.hot) {
    // HotModuleReplacementPlugin is not in Webpack configuration.
    window.location.reload();
    return;
  }

  if (!isUpdateAvailable() || !canApplyUpdates()) {
    return;
  }

  function handleApplyUpdates(err, updatedModules) {
    const hasReactRefresh = process.env.FAST_REFRESH;
    const wantsForcedReload = err || !updatedModules || hadRuntimeError;
    // React refresh can handle hot-reloading over errors.
    if (!hasReactRefresh && wantsForcedReload) {
      window.location.reload();
      return;
    }

    if (typeof onHotUpdateSuccess === 'function') {
      // Maybe we want to do something.
      onHotUpdateSuccess();
    }

    if (isUpdateAvailable()) {
      // While we were updating, there was a new update! Do it again.
      tryApplyUpdates();
    }
  }

  // https://webpack.github.io/docs/hot-module-replacement.html#check
  var result = module.hot.check(/* autoApply */ true, handleApplyUpdates);

  // // Webpack 2 returns a Promise instead of invoking a callback
  if (result && result.then) {
    result.then(
      function(updatedModules) {
        handleApplyUpdates(null, updatedModules);
      },
      function(err) {
        handleApplyUpdates(err, null);
      }
    );
  }
}
<commit_msg>- Removed extraneous `console.error()`<commit_after>'use strict';

// This alternative WebpackDevServer combines the functionality of:
// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js

// It only supports their simplest configuration (hot updates on same server).
// It makes some opinionated choices on top, like adding a syntax error overlay
// that looks similar to our console output. The error overlay is inspired by:
// https://github.com/glenjamin/webpack-hot-middleware

var SockJS = require('sockjs-client');
var stripAnsi = require('strip-ansi');
var url = require('url');
var launchEditorEndpoint = require('react-dev-utils/launchEditorEndpoint');
var devServerMajorVersion = require('./devServerMajor');
var formatWebpackMessages = require('./formatWebpackMessages');
var ErrorOverlay = require('react-error-overlay');

var socketUrl = createSocketUrl();
var parsedSocketUrl = url.parse(socketUrl);

var createSocketUrl;
if (devServerMajorVersion > 3) {
  // The path changed with v4
  createSocketUrl = require('webpack-dev-server/client/utils/createSocketURL');
} else {
  createSocketUrl = require('webpack-dev-server/client/utils/createSocketUrl');
}

ErrorOverlay.setEditorHandler(function editorHandler(errorLocation) {
  // Keep this sync with errorOverlayMiddleware.js
  fetch(
    url.format({
      protocol: parsedSocketUrl.protocol,
      hostname: parsedSocketUrl.hostname,
      port: parsedSocketUrl.port,
      pathname: launchEditorEndpoint,
      search:
        '?fileName=' +
        window.encodeURIComponent(errorLocation.fileName) +
        '&lineNumber=' +
        window.encodeURIComponent(errorLocation.lineNumber || 1) +
        '&colNumber=' +
        window.encodeURIComponent(errorLocation.colNumber || 1),
    }),
    { mode: 'no-cors' }
  );
});

// We need to keep track of if there has been a runtime error.
// Essentially, we cannot guarantee application state was not corrupted by the
// runtime error. To prevent confusing behavior, we forcibly reload the entire
// application. This is handled below when we are notified of a compile (code
// change).
// See https://github.com/facebookincubator/create-react-app/issues/3096
var hadRuntimeError = false;
ErrorOverlay.startReportingRuntimeErrors({
  onError: function() {
    hadRuntimeError = true;
  },
  filename: process.env.REACT_BUNDLE_PATH || '/static/js/bundle.js',
});

if (module.hot && typeof module.hot.dispose === 'function') {
  module.hot.dispose(function() {
    // TODO: why do we need this?
    ErrorOverlay.stopReportingRuntimeErrors();
  });
}

// Connect to WebpackDevServer via a socket.
var connection = new SockJS(socketUrl);

// Unlike WebpackDevServer client, we won't try to reconnect
// to avoid spamming the console. Disconnect usually happens
// when developer stops the server.
connection.onclose = function() {
  if (typeof console !== 'undefined' && typeof console.info === 'function') {
    console.info(
      'The development server has disconnected.\nRefresh the page if necessary.'
    );
  }
};

// Remember some state related to hot module replacement.
var isFirstCompilation = true;
var mostRecentCompilationHash = null;
var hasCompileErrors = false;

function clearOutdatedErrors() {
  // Clean up outdated compile errors, if any.
  if (typeof console !== 'undefined' && typeof console.clear === 'function') {
    if (hasCompileErrors) {
      console.clear();
    }
  }
}

// Successful compilation.
function handleSuccess() {
  clearOutdatedErrors();

  var isHotUpdate = !isFirstCompilation;
  isFirstCompilation = false;
  hasCompileErrors = false;

  // Attempt to apply hot updates or reload.
  if (isHotUpdate) {
    tryApplyUpdates(function onHotUpdateSuccess() {
      // Only dismiss it when we're sure it's a hot update.
      // Otherwise it would flicker right before the reload.
      tryDismissErrorOverlay();
    });
  }
}

// Compilation with warnings (e.g. ESLint).
function handleWarnings(warnings) {
  clearOutdatedErrors();

  var isHotUpdate = !isFirstCompilation;
  isFirstCompilation = false;
  hasCompileErrors = false;

  function printWarnings() {
    // Print warnings to the console.
    var formatted = formatWebpackMessages({
      warnings: warnings,
      errors: [],
    });

    if (typeof console !== 'undefined' && typeof console.warn === 'function') {
      for (var i = 0; i < formatted.warnings.length; i++) {
        if (i === 5) {
          console.warn(
            'There were more warnings in other files.\n' +
              'You can find a complete log in the terminal.'
          );
          break;
        }
        console.warn(stripAnsi(formatted.warnings[i]));
      }
    }
  }

  // Attempt to apply hot updates or reload.
  if (isHotUpdate) {
    tryApplyUpdates(function onSuccessfulHotUpdate() {
      // Only print warnings if we aren't refreshing the page.
      // Otherwise they'll disappear right away anyway.
      printWarnings();
      // Only dismiss it when we're sure it's a hot update.
      // Otherwise it would flicker right before the reload.
      tryDismissErrorOverlay();
    });
  } else {
    // Print initial warnings immediately.
    printWarnings();
  }
}

// Compilation with errors (e.g. syntax error or missing modules).
function handleErrors(errors) {
  clearOutdatedErrors();

  isFirstCompilation = false;
  hasCompileErrors = true;

  // "Massage" webpack messages.
  var formatted = formatWebpackMessages({
    errors: errors,
    warnings: [],
  });

  // Only show the first error.
  ErrorOverlay.reportBuildError(formatted.errors[0]);

  // Also log them to the console.
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    for (var i = 0; i < formatted.errors.length; i++) {
      console.error(stripAnsi(formatted.errors[i]));
    }
  }

  // Do not attempt to reload now.
  // We will reload on next success instead.
}

function tryDismissErrorOverlay() {
  if (!hasCompileErrors) {
    ErrorOverlay.dismissBuildError();
  }
}

// There is a newer version of the code available.
function handleAvailableHash(hash) {
  // Update last known compilation hash.
  mostRecentCompilationHash = hash;
}

// Handle messages from the server.
connection.onmessage = function(e) {
  var message = JSON.parse(e.data);
  switch (message.type) {
    case 'hash':
      handleAvailableHash(message.data);
      break;
    case 'still-ok':
    case 'ok':
      handleSuccess();
      break;
    case 'content-changed':
      // Triggered when a file from `contentBase` changed.
      window.location.reload();
      break;
    case 'warnings':
      handleWarnings(message.data);
      break;
    case 'errors':
      handleErrors(message.data);
      break;
    default:
    // Do nothing.
  }
};

// Is there a newer version of this code available?
function isUpdateAvailable() {
  /* globals __webpack_hash__ */
  // __webpack_hash__ is the hash of the current compilation.
  // It's a global variable injected by Webpack.
  return mostRecentCompilationHash !== __webpack_hash__;
}

// Webpack disallows updates in other states.
function canApplyUpdates() {
  return module.hot.status() === 'idle';
}

// Attempt to update code on the fly, fall back to a hard reload.
function tryApplyUpdates(onHotUpdateSuccess) {
  if (!module.hot) {
    // HotModuleReplacementPlugin is not in Webpack configuration.
    window.location.reload();
    return;
  }

  if (!isUpdateAvailable() || !canApplyUpdates()) {
    return;
  }

  function handleApplyUpdates(err, updatedModules) {
    const hasReactRefresh = process.env.FAST_REFRESH;
    const wantsForcedReload = err || !updatedModules || hadRuntimeError;
    // React refresh can handle hot-reloading over errors.
    if (!hasReactRefresh && wantsForcedReload) {
      window.location.reload();
      return;
    }

    if (typeof onHotUpdateSuccess === 'function') {
      // Maybe we want to do something.
      onHotUpdateSuccess();
    }

    if (isUpdateAvailable()) {
      // While we were updating, there was a new update! Do it again.
      tryApplyUpdates();
    }
  }

  // https://webpack.github.io/docs/hot-module-replacement.html#check
  var result = module.hot.check(/* autoApply */ true, handleApplyUpdates);

  // // Webpack 2 returns a Promise instead of invoking a callback
  if (result && result.then) {
    result.then(
      function(updatedModules) {
        handleApplyUpdates(null, updatedModules);
      },
      function(err) {
        handleApplyUpdates(err, null);
      }
    );
  }
}
<|endoftext|>


<commit_before>/**
 * Temporal coverage search facet
 */
define([
    'logger',
    'jquery',
    'moment',
    'i18n',
    'bootstrap-datepicker'
], function(log, $, moment, i18n) {
    'use strict';

    // Use the same localization than moment
    $.fn.datepicker.dates[i18n.lang] = {
        days: moment.weekdays(),
        daysShort: moment.weekdaysShort(),
        daysMin: moment.weekdaysMin(),
        months: moment.months(),
        monthsShort: moment.monthsShort(),
        today: i18n._('Today'),
        clear: i18n._('Clear')
    };

    $('.temporal-coverage').each(function() {
        var $panel = $(this),
            $picker = $panel.find('.facet-datepicker'),
            $current = null;

        $panel.find('.input-daterange input').each(function() {
            var $this = $(this);

            // Set initial value
            $this.val(moment($this.data('isodate')).format('L'));

            $this.on('click focus', function() {
                var dt = moment($this.data('isodate'));

                $current = $this;
                $picker.datepicker('update', dt.toDate())
                $panel.find('.hide').removeClass('hide');
            });
        });

        $picker
            .datepicker({
                format: "yyyy-mm-dd",
                language: i18n.lang,
                weekStart: moment.langData()._week.dow
            })
            .on('changeDate', function(e) {
                if ($current) {
                    var dt = moment(e.date);
                    $current.data('isodate', dt.format('YYYY-MM-DD'));
                    $current.val(dt.format('L'));
                    $picker.addClass('hide');
                }
            });

        // Submit search on "Apply click"
        $panel.find('.btn-apply').click(function() {
            var filter = [
                $panel.find('[name=start]').data('isodate'),
                $panel.find('[name=end]').data('isodate')
            ].join('-')
            window.location = $panel.data('url-pattern').replace('__r__', filter);
        });

    });

});
<commit_msg>Handle user input<commit_after>/**
 * Temporal coverage search facet
 */
define([
    'logger',
    'jquery',
    'moment',
    'i18n',
    'bootstrap-datepicker'
], function(log, $, moment, i18n) {
    'use strict';

    // Use the same localization than moment
    $.fn.datepicker.dates[i18n.lang] = {
        days: moment.weekdays(),
        daysShort: moment.weekdaysShort(),
        daysMin: moment.weekdaysMin(),
        months: moment.months(),
        monthsShort: moment.monthsShort(),
        today: i18n._('Today'),
        clear: i18n._('Clear')
    };

    $('.temporal-coverage').each(function() {
        var $panel = $(this),
            $picker = $panel.find('.facet-datepicker'),
            $current = null;

        $panel.find('.input-daterange input').each(function() {
            var $this = $(this);

            // Set initial value
            $this.val(moment($this.data('isodate')).format('L'));

            // Handle picker displya on input or focus
            $this.on('click focus', function() {
                var dt = moment($this.data('isodate'));

                $current = $this;
                $picker.datepicker('update', dt.toDate())
                $panel.find('.hide').removeClass('hide');
            })
            // Handle user input
            .on('input', function() {
                var dt = moment($this.val(), 'L', true);
                if (dt.isValid()) {
                    $this.data('isodate', dt.format('YYYY-MM-DD'));
                    if ($this == $current && !$picker.hasClass('hide')) {
                        $picker.datepicker('update', dt.toDate());
                        $picker.addClass('hide');
                    }
                }
            });

        });

        $picker
            .datepicker({
                format: "yyyy-mm-dd",
                language: i18n.lang,
                weekStart: moment.langData()._week.dow
            })
            .on('changeDate', function(e) {
                if ($current) {
                    var dt = moment(e.date);
                    $current.data('isodate', dt.format('YYYY-MM-DD'));
                    $current.val(dt.format('L'));
                    $picker.addClass('hide');
                }
            });

        // Submit search on "Apply click"
        $panel.find('.btn-apply').click(function() {
            var filter = [
                $panel.find('[name=start]').data('isodate'),
                $panel.find('[name=end]').data('isodate')
            ].join('-')
            window.location = $panel.data('url-pattern').replace('__r__', filter);
        });

    });

});
<|endoftext|>


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

  var pi = Math.PI;
  var PIXELS_PER_AU = 50;
  var USE_REAL_ELLIPSE = true;

  var attributes
  var uniforms;

  var Orbit3D = function(eph, opts, bigParticle) {
    opts = opts || {};
    opts.color = opts.color || 0xffee00;
    opts.width = opts.width || 1;
    opts.object_size = opts.object_size || 1;
    opts.jed =  opts.jed || 2451545.0;

    this.opts = opts;
    this.eph = eph;
    this.particle_geometry = opts.particle_geometry;
    this.bigParticle = bigParticle;

    this.CreateParticle(opts.jed, opts.texture_path);
  }

  Orbit3D.prototype.CreateOrbit = function(jed) {
    var pts;
    var points;
    var time = jed;
    var pts = []
    var limit = this.eph.P ? this.eph.P+1 : this.eph.per;
    var parts = this.eph.e > .20 ? 300 : 100;   // extra precision for high eccentricity
    var delta = Math.ceil(limit / parts);
    var prev;
    for (var i=0; i <= parts; i++, time+=delta) {
      var pos = this.getPosAtTime(time);
      var vector = new THREE.Vector3(pos[0], pos[1], pos[2]);
      prev = vector;
      pts.push(vector);
    }

    points = new THREE.Geometry();
    points.vertices = pts;
    points.computeLineDistances(); // required for dotted lines

    var line = new THREE.Line(points,
      new THREE.LineDashedMaterial({
        color: this.opts.color,
        linewidth: this.opts.width,
        dashSize: 1,
        gapSize: 0.5,
      }), THREE.LineStrip);
    return line;
  }

  Orbit3D.prototype.CreateParticle = function(jed, texture_path) {
    if (!this.bigParticle && this.particle_geometry) {
      // dummy position for particle geometry
      var tmp_vec = new THREE.Vector3(0,0,0);
      this.particle_geometry.vertices.push(tmp_vec);
      return;
    }

    // this is used by broken canvas mode only

    var pos = this.getPosAtTime(jed);

    if (this.bigParticle) {
      //var obj = new THREE.Object3D();
      var geometry = new THREE.SphereGeometry(this.opts.object_size);
      //var geometry = new THREE.CubeGeometry(100, 100, 100);
      var mat_opts = {color: this.opts.color};
      if (texture_path) {
        $.extend(mat_opts, {
          map: THREE.ImageUtils.loadTexture(texture_path),
          wireframe: false,
          overdraw: true
        });
      }
      var material= new THREE.MeshBasicMaterial(mat_opts);
      this.particle = new THREE.Mesh(geometry, material);
      //this.particle.scale.x = -1; // flip so texture shows up oriented correctly
      this.particle.position.set(pos[0], pos[1], pos[2]);
    }
  }

  Orbit3D.prototype.MoveParticle = function(time_jed) {
    var pos = this.getPosAtTime(time_jed);
    this.MoveParticleToPosition(pos);
  }

  Orbit3D.prototype.MoveParticleToPosition = function(pos) {
    if (this.bigParticle) {
      this.particle.position.set(pos[0], pos[1], pos[2]);
    }
    else {
      var vertex_particle = this.particle_geometry.vertices[this.vertex_pos];
      vertex_particle.x = pos[0];
      vertex_particle.y = pos[1];
      vertex_particle.z = pos[2];
    }
  }

  Orbit3D.prototype.getPosAtTime = function(jed) {
    // Note: this must match the vertex shader.
    // This position calculation is used to follow asteroids in 'lock-on' mode
    var e = this.eph.e;
    var a = this.eph.a;
    var i = (this.eph.i-Ephemeris.earth.i) * pi/180;
    var o = (this.eph.om-Ephemeris.earth.om) * pi/180; // longitude of ascending node
    var p = this.eph.w * pi/180; // longitude of perihelion
    var ma = this.eph.ma;
    var M;
    // Calculate mean anomaly at J2000
    ma = ma * pi/180;
    var n;
    if (this.eph.n)
      n = this.eph.n * pi/180; // mean motion
    else {
      n = 2*pi / this.eph.P;
    }
    var epoch = this.eph.epoch;
    var d = epoch - jed;
    M = ma + n * d;

    var sin = Math.sin, cos = Math.cos;
    // Estimate eccentric and true anom using iterative approx
    var E0 = M;
    var lastdiff;
    do {
      var E1 = M + e * sin(E0);
      lastdiff = Math.abs(E1-E0);
      E0 = E1;
    } while(lastdiff > 0.0000001);
    var E = E0;
    var v = 2 * Math.atan(Math.sqrt((1+e)/(1-e)) * Math.tan(E/2));

    // radius vector, in AU
    var r = a * (1 - e*e) / (1 + e * cos(v)) * PIXELS_PER_AU;

    // heliocentric coords
    var X = r * (cos(o) * cos(v + p - o) - sin(o) * sin(v + p - o) * cos(i))
    var Y = r * (sin(o) * cos(v + p - o) + cos(o) * sin(v + p - o) * cos(i))
    var Z = r * (sin(v + p - o) * sin(i))
    var ret = [X, Y, Z];
    return ret;
  }

  Orbit3D.prototype.getEllipse = function() {
    if (!this.ellipse)
      this.ellipse = this.CreateOrbit(this.opts.jed);
    return this.ellipse;
  }

  Orbit3D.prototype.getParticle = function() {
    return this.particle;
  }

  window.Orbit3D = Orbit3D;
})();

// Data from NASA, eg.
// http://nssdc.gsfc.nasa.gov/planetary/factsheet/marsfact.html

(function() {
  var eph = {
    mercury: {
      full_name: 'Mercury',
      ma: 174.79439,
      epoch: 2451545.0,
      a: 0.38709893,
      e: 0.20563069,
      i: 7.00487,
      w: 77.45645,
      L: 252.25084,
      om: 48.33167,
      P: 87.969
    },
    venus: {
      full_name: 'Venus',
      ma: 50.37663231999999,
      epoch: 2451545.0,
      a:0.72333566,
      e:0.00677672,
      i:3.39467605,
      w:131.60246718,
      L:181.97909950,
      om:76.67984255,
      P: 224.701
    },
    earth: {
      full_name: 'Earth',
      ma: -2.4731102699999905,
      epoch: 2451545.0,
      a:1.00000261,
      e:0.01671123,
      i:-0.00001531,
      w:102.93768193,
      L:100.46457166,
      //om:-11.26064,
      om: 0,
      P: 365.256
    },
    mars:{
      full_name: 'Mars',
      ma: 19.412479999999945,
      epoch: 2451545.0,
      a: 1.52366231,
      e: 0.09341233,
      i: 1.85061,
      w: 336.04084,
      L:355.45332,
      om:49.57854,
      P: 686.980
    },
    jupiter: {
      full_name: 'Jupiter',
      ma: 19.66796068,
      epoch: 2451545.0,
      a:5.20288700,
      e:0.04838624,
      i:1.30439695,
      w:14.72847983,
      L:34.39644051,
      om:100.47390909,
      P: 4332.589
    },
  };
  window.Ephemeris = eph;
})();
<commit_msg>more cleanup<commit_after>(function() {
  "use strict";

  var pi = Math.PI;
  var PIXELS_PER_AU = 50;
  var USE_REAL_ELLIPSE = true;

  var attributes
  var uniforms;

  var Orbit3D = function(eph, opts, bigParticle) {
    opts = opts || {};
    opts.color = opts.color || 0xffee00;
    opts.width = opts.width || 1;
    opts.object_size = opts.object_size || 1;
    opts.jed =  opts.jed || 2451545.0;

    this.opts = opts;
    this.eph = eph;
    this.particle_geometry = opts.particle_geometry;
    this.bigParticle = bigParticle;

    this.CreateParticle(opts.jed, opts.texture_path);
  }

  Orbit3D.prototype.CreateOrbit = function(jed) {
    var pts;
    var points;
    var time = jed;
    var pts = []
    var limit = this.eph.P ? this.eph.P+1 : this.eph.per;
    var parts = this.eph.e > .20 ? 300 : 100;   // extra precision for high eccentricity
    var delta = Math.ceil(limit / parts);
    var prev;
    for (var i=0; i <= parts; i++, time+=delta) {
      var pos = this.getPosAtTime(time);
      var vector = new THREE.Vector3(pos[0], pos[1], pos[2]);
      prev = vector;
      pts.push(vector);
    }

    points = new THREE.Geometry();
    points.vertices = pts;
    points.computeLineDistances(); // required for dotted lines

    var line = new THREE.Line(points,
      new THREE.LineDashedMaterial({
        color: this.opts.color,
        linewidth: this.opts.width,
        dashSize: 1,
        gapSize: 0.5,
      }), THREE.LineStrip);
    return line;
  }

  Orbit3D.prototype.CreateParticle = function(jed, texture_path) {
    if (!this.bigParticle && this.particle_geometry) {
      // dummy position for particle geometry
      var tmp_vec = new THREE.Vector3(0,0,0);
      this.particle_geometry.vertices.push(tmp_vec);
      return;
    }

    // this is used by broken canvas mode only

    var pos = this.getPosAtTime(jed);

    if (this.bigParticle) {
      //var obj = new THREE.Object3D();
      var geometry = new THREE.SphereGeometry(this.opts.object_size);
      //var geometry = new THREE.CubeGeometry(100, 100, 100);
      var mat_opts = {color: this.opts.color};
      if (texture_path) {
        $.extend(mat_opts, {
          map: THREE.ImageUtils.loadTexture(texture_path),
          wireframe: false,
          overdraw: true
        });
      }
      var material= new THREE.MeshBasicMaterial(mat_opts);
      this.particle = new THREE.Mesh(geometry, material);
      //this.particle.scale.x = -1; // flip so texture shows up oriented correctly
      this.particle.position.set(pos[0], pos[1], pos[2]);
    }
  }

  Orbit3D.prototype.MoveParticle = function(time_jed) {
    var pos = this.getPosAtTime(time_jed);
    this.MoveParticleToPosition(pos);
  }

  Orbit3D.prototype.MoveParticleToPosition = function(pos) {
    if (this.bigParticle) {
      this.particle.position.set(pos[0], pos[1], pos[2]);
    }
    else {
      var vertex_particle = this.particle_geometry.vertices[this.vertex_pos];
      vertex_particle.x = pos[0];
      vertex_particle.y = pos[1];
      vertex_particle.z = pos[2];
    }
  }

  Orbit3D.prototype.getPosAtTime = function(jed) {
    // Note: this must match the vertex shader.
    // This position calculation is used to follow asteroids in 'lock-on' mode
    var e = this.eph.e;
    var a = this.eph.a;
    var i = (this.eph.i-Ephemeris.earth.i) * pi/180;
    var o = (this.eph.om-Ephemeris.earth.om) * pi/180; // longitude of ascending node
    var p = this.eph.w * pi/180; // longitude of perihelion
    var ma = this.eph.ma;
    var M;
    // Calculate mean anomaly at J2000
    ma = ma * pi/180;
    var n;
    if (this.eph.n)
      n = this.eph.n * pi/180; // mean motion
    else {
      n = 2*pi / this.eph.P;
    }
    var epoch = this.eph.epoch;
    var d = epoch - jed;
    M = ma + n * d;

    var sin = Math.sin, cos = Math.cos;
    // Estimate eccentric and true anom using iterative approx
    var E0 = M;
    var lastdiff;
    do {
      var E1 = M + e * sin(E0);
      lastdiff = Math.abs(E1-E0);
      E0 = E1;
    } while(lastdiff > 0.0000001);
    var E = E0;
    var v = 2 * Math.atan(Math.sqrt((1+e)/(1-e)) * Math.tan(E/2));

    // radius vector, in AU
    var r = a * (1 - e*e) / (1 + e * cos(v)) * PIXELS_PER_AU;

    // heliocentric coords
    var X = r * (cos(o) * cos(v + p - o) - sin(o) * sin(v + p - o) * cos(i))
    var Y = r * (sin(o) * cos(v + p - o) + cos(o) * sin(v + p - o) * cos(i))
    var Z = r * (sin(v + p - o) * sin(i))
    var ret = [X, Y, Z];
    return ret;
  }

  Orbit3D.prototype.getEllipse = function() {
    if (!this.ellipse)
      this.ellipse = this.CreateOrbit(this.opts.jed);
    return this.ellipse;
  }

  Orbit3D.prototype.getParticle = function() {
    return this.particle;
  }

  window.Orbit3D = Orbit3D;
})();

// Data from NASA, eg.
// http://nssdc.gsfc.nasa.gov/planetary/factsheet/marsfact.html

window.Ephemeris = {
  mercury: {
    full_name: 'Mercury',
    ma: 174.79439,
    epoch: 2451545.0,
    a: 0.38709893,
    e: 0.20563069,
    i: 7.00487,
    w: 77.45645,
    L: 252.25084,
    om: 48.33167,
    P: 87.969
  },
  venus: {
    full_name: 'Venus',
    ma: 50.37663231999999,
    epoch: 2451545.0,
    a:0.72333566,
    e:0.00677672,
    i:3.39467605,
    w:131.60246718,
    L:181.97909950,
    om:76.67984255,
    P: 224.701
  },
  earth: {
    full_name: 'Earth',
    ma: -2.4731102699999905,
    epoch: 2451545.0,
    a:1.00000261,
    e:0.01671123,
    i:-0.00001531,
    w:102.93768193,
    L:100.46457166,
    //om:-11.26064,
    om: 0,
    P: 365.256
  },
  mars:{
    full_name: 'Mars',
    ma: 19.412479999999945,
    epoch: 2451545.0,
    a: 1.52366231,
    e: 0.09341233,
    i: 1.85061,
    w: 336.04084,
    L:355.45332,
    om:49.57854,
    P: 686.980
  },
  jupiter: {
    full_name: 'Jupiter',
    ma: 19.66796068,
    epoch: 2451545.0,
    a:5.20288700,
    e:0.04838624,
    i:1.30439695,
    w:14.72847983,
    L:34.39644051,
    om:100.47390909,
    P: 4332.589
  }
};
<|endoftext|>