<commit_before>(function() {

  function Queue() {

    // callback storage
    this._methods = [];

    // reference to the response
    this._response = null;

    // all queues start off unflushed
    this._flushed = false;

  };

  Queue.prototype = {

    // adds callbacks to the queue
    add: function(fn) {

      // if the queue had been flushed, return immediately
      if (this._flushed) {

        // otherwise push it on the queue
        fn(this._response);

      } else {
        this._methods.push(fn);
      }

    },

    flush: function(resp) {

      // flush only ever happens once
      if (this._flushed) {
        return;
      }

      // store the response for subsequent calls after flush()
      this._response = resp;

      // mark that it's been flushed
      this._flushed = true;

      // shift 'em out and call 'em back
      while (this._methods[0]) {
        this._methods.shift()(resp);
      }

    }

  };

  var Image = function() {

    Map.call(this, this);

    this.imageOptions = {};

    this.error = null;

    this.supported_formats = ["png", "jpg"];

    this.defaults = {
      basemap_url_template: "http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
      basemap_subdomains: ["a", "b", "c"],
      format: "png",
      zoom: 10,
      center: [0, 0],
      size:  [320, 240],
      tiler_port: 80,
      tiler_domain: "cartodb.com"
    };

  };

  Image.prototype = _.extend({}, Map.prototype, {

    load: function(vizjson, options) {

      _.bindAll(this, "_onVisLoaded");

      this.queue = new Queue;

      this.userOptions = options;

      options = _.defaults({ vizjson: vizjson, temp_id: "s" + this._getUUID() }, this.defaults);

      this.imageOptions = options;

      cdb.core.Loader.get(vizjson, this._onVisLoaded);

      return this;

    },

    loadLayerDefinition: function(layerDefinition) {

      var self = this;

      this.queue = new Queue;

      if (!layerDefinition.user_name) {
        cartodb.log.error("Please, specify the username");
        return;
      }

      this.options.user_name      = layerDefinition.user_name;
      this.options.tiler_protocol = layerDefinition.tiler_protocol;
      this.options.tiler_domain   = layerDefinition.tiler_domain;
      this.options.tiler_port     = layerDefinition.tiler_port;
      this.endPoint = "/api/v1/map";

      this.options.layers = layerDefinition;

      this._requestLayerGroupID();

    },

    _onVisLoaded: function(data) {

      if (data) {

        var layerDefinition;
        var baseLayer = data.layers[0];
        var dataLayer = data.layers[1];

        this.options.user_name = dataLayer.options.user_name;

        this._setupTilerConfiguration(dataLayer.options.tiler_protocol, dataLayer.options.tiler_domain, dataLayer.options.tiler_port);

        this.endPoint = "/api/v1/map";

        var bbox = [];

        var bounds = data.bounds;

        if (bounds) {
          bbox.push([bounds[0][1], bounds[0][0]]);
          bbox.push([bounds[1][1], bounds[1][0]]);
        }

        this.imageOptions.zoom   = data.zoom;
        this.imageOptions.center = JSON.parse(data.center);
        this.imageOptions.bbox   = bbox;
        this.imageOptions.bounds = data.bounds;

        if (baseLayer && baseLayer.options) {
          this.imageOptions.basemap = baseLayer;
        }

        var layers = [ this._getBasemapLayer() ];

        for (var i = 1; i < data.layers.length; i++) {

          var layer = data.layers[i];

          if (layer.type === "torque") {
            layers.push(this._getTorqueLayerDefinition(layer));
          } else if (layer.type === "namedmap") {
            layers.push(this._getNamedmapLayerDefinition(layer));
          } else {
            var ll = this._getLayergroupLayerDefinition(layer);
            for (var j = 0; j < ll.length; j++) {
              layers.push(ll[j]);
            }
          }
        }

        this.options.layers = { layers: layers };
        this._requestLayerGroupID();

      }

    },

    _setupTilerConfiguration: function(protocol, domain, port) {

      this.options.tiler_domain   = domain;
      this.options.tiler_protocol = protocol;
      this.options.tiler_port     = port;

      if (this.userOptions.https || this.imageOptions.vizjson.indexOf("https") === 0) {
        this.options.tiler_protocol = "https";
        this.options.tiler_port     = 443;
      }

    },

    toJSON: function(){
      return this.options.layers;
    },

    _requestLayerGroupID: function() {

      var self = this;

      this._requestPOST({}, function(data, error) {

        if (error) {
          self.error = error;
        }

        if (data) {
          self.imageOptions.layergroupid = data.layergroupid;
        }

        self.queue.flush(this);

      });

    },

    _getDefaultBasemapLayer: function() {

      return {
        type: "http",
        options: {
          urlTemplate: this.defaults.basemap_url_template,
          subdomains:  this.defaults.basemap_subdomains
        }
      };

    },

    _getHTTPBasemapLayer: function(basemap) {

      return {
        type: "http",
        options: {
          urlTemplate: basemap.options.urlTemplate,
          subdomains: basemap.options.subdomains || this.defaults.basemap_subdomains
        }
      };

    },

    _getPlainBasemapLayer: function(color) {

      return {
        type: "plain",
        options: {
          color: color
        }
      };

    },

    _getBasemapLayer: function() {

      var basemap = this.userOptions.basemap || this.imageOptions.basemap;

      if (basemap) {

        // TODO: refactor this
        var type = basemap.type.toLowerCase();

        if (basemap.options && basemap.options.type) {
          type = basemap.options.type.toLowerCase();
        }

        if (type === "plain") {
          return this._getPlainBasemapLayer(basemap.options.color);
        } else {
          return this._getHTTPBasemapLayer(basemap);
        }

      }

      return this._getDefaultBasemapLayer();

    },

    _getTorqueLayerDefinition: function(layer_definition) {

      var layerDefinition = new LayerDefinition(layer_definition, layer_definition.options);

      var query    = layerDefinition.options.query || "SELECT * FROM " + layerDefinition.options.table_name;
      var cartocss = layer_definition.options.tile_style;

      return {
        type: "torque",
        options: {
          sql: query,
          cartocss: cartocss
        }
      };

    },

    _getLayergroupLayerDefinition: function(layer) {

      var options = layer.options;
      var layerDefinition = new LayerDefinition(options.layer_definition, options);

      return layerDefinition.toJSON().layers;

    },

    _getNamedmapLayerDefinition: function(layer) {

      var options = layer.options;

      var layerDefinition = new NamedMap(options.named_map, options);

      //layerDefinition.options.type = "named";

      return { type: "named",
        options: {
          name: layerDefinition.named_map.name
        }
      }

    },

    _getUrl: function() {

      var username     = this.options.user_name;
      var bbox         = this.imageOptions.bbox;
      var layergroupid = this.imageOptions.layergroupid;
      var zoom         = this.imageOptions.zoom   || this.defaults.zoom;
      var center       = this.imageOptions.center || this.defaults.center;
      var size         = this.imageOptions.size   || this.defaults.size;
      var format       = this.imageOptions.format || this.defaults.format;

      var lat    = center[0];
      var lon    = center[1];

      var width  = size[0];
      var height = size[1];

      var url = this._tilerHost() + this.endPoint;

      if (bbox && bbox.length) {
        return [url, "static/bbox" , layergroupid, bbox.join(","), width, height + "." + format].join("/");
      } else {
        return [url, "static/center" , layergroupid, zoom, lat, lon, width, height + "." + format].join("/");
      }

    },

    // Generates a random string
    _getUUID: function() {
      var S4 = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
      };
      return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
    },

    /* Setters */
    _set: function(name, value) {

      var self = this;

      this.queue.add(function() {
        self.imageOptions[name] = value;
      });

      return this;

    },

    zoom: function(zoom) {
      return this._set("zoom", zoom);
    },

    bbox: function(bbox) {
      return this._set("bbox", bbox);
    },

    center: function(center) {
      this._set("bbox", null);
      return this._set("center", center);
    },

    format: function(format) {
      return this._set("format", _.include(this.supported_formats, format) ? format : this.defaults.format);
    },

    size: function(width, height) {
      return this._set("size", [width, height === undefined ? width : height]);
    },

    /* Methods */

    /* Image.into(HTMLImageElement)
       inserts the image in the HTMLImageElement specified */
    into: function(img) {

      var self = this;

      if (!(img instanceof HTMLImageElement)) {
        cartodb.log.error("img should be an image");
        return;
      }

      this.imageOptions.size = [img.width, img.height];

      this.queue.add(function(response) {
        img.src = self._getUrl();
      });

    },

    /* Image.getUrl(callback(err, url))
       gets the url for the image, err is null is there was no error */

    getUrl: function(callback) {

      var self = this;

      this.queue.add(function() {
        if (callback) {
          callback(self.error, self._getUrl());
        }
      });

    },

    /* Image.write(attributes)
       adds a img tag in the same place script is executed */

    write: function(attributes) {

      var self = this;

      this.imageOptions.attributes = attributes;

      if (attributes && attributes.src) {
        document.write('');
      } else {
        document.write('');
      }

      this.queue.add(function() {

        var element = document.getElementById(self.imageOptions.temp_id);

        element.src = self._getUrl();
        element.removeAttribute("temp_id");

        var attributes = self.imageOptions.attributes;

        if (attributes && attributes.class) { element.setAttribute("class", attributes.class); }
        if (attributes && attributes.id)    { element.setAttribute("id", attributes.id); }

      });

      return this;
    }

  })

  cdb.Image = function(data, options) {

    if (!options) options = {};

    var image = new Image();

    if (typeof data === 'string') {
      image.load(data, options);
    } else {
      image.loadLayerDefinition(data);
    }

    return image;

  };

})();
<commit_msg>Fixes #354<commit_after>(function() {

  function Queue() {

    // callback storage
    this._methods = [];

    // reference to the response
    this._response = null;

    // all queues start off unflushed
    this._flushed = false;

  };

  Queue.prototype = {

    // adds callbacks to the queue
    add: function(fn) {

      // if the queue had been flushed, return immediately
      if (this._flushed) {

        // otherwise push it on the queue
        fn(this._response);

      } else {
        this._methods.push(fn);
      }

    },

    flush: function(resp) {

      // flush only ever happens once
      if (this._flushed) {
        return;
      }

      // store the response for subsequent calls after flush()
      this._response = resp;

      // mark that it's been flushed
      this._flushed = true;

      // shift 'em out and call 'em back
      while (this._methods[0]) {
        this._methods.shift()(resp);
      }

    }

  };

  var Image = function() {

    Map.call(this, this);

    this.imageOptions = {};

    this.error = null;

    this.supported_formats = ["png", "jpg"];

    this.defaults = {
      basemap_url_template: "http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
      basemap_subdomains: ["a", "b", "c"],
      format: "png",
      zoom: 10,
      center: [0, 0],
      size:  [320, 240],
      tiler_port: 80,
      tiler_domain: "cartodb.com"
    };

  };

  Image.prototype = _.extend({}, Map.prototype, {

    load: function(vizjson, options) {

      _.bindAll(this, "_onVisLoaded");

      this.queue = new Queue;

      this.userOptions = options;

      options = _.defaults({ vizjson: vizjson, temp_id: "s" + this._getUUID() }, this.defaults);

      this.imageOptions = options;

      cdb.core.Loader.get(vizjson, this._onVisLoaded);

      return this;

    },

    loadLayerDefinition: function(layerDefinition) {

      var self = this;

      this.queue = new Queue;

      if (!layerDefinition.user_name) {
        cartodb.log.error("Please, specify the username");
        return;
      }

      this.options.user_name      = layerDefinition.user_name;
      this.options.tiler_protocol = layerDefinition.tiler_protocol;
      this.options.tiler_domain   = layerDefinition.tiler_domain;
      this.options.tiler_port     = layerDefinition.tiler_port;
      this.endPoint = "/api/v1/map";

      this.options.layers = layerDefinition;

      this._requestLayerGroupID();

    },

    _onVisLoaded: function(data) {

      if (data) {

        var layerDefinition;
        var baseLayer = data.layers[0];
        var dataLayer = data.layers[1];

        this.options.user_name = dataLayer.options.user_name;

        this._setupTilerConfiguration(dataLayer.options.tiler_protocol, dataLayer.options.tiler_domain, dataLayer.options.tiler_port);

        this.endPoint = "/api/v1/map";

        var bbox = [];

        var bounds = data.bounds;

        if (bounds) {
          bbox.push([bounds[0][1], bounds[0][0]]);
          bbox.push([bounds[1][1], bounds[1][0]]);
        }

        this.imageOptions.zoom   = data.zoom;
        this.imageOptions.center = JSON.parse(data.center);
        this.imageOptions.bbox   = bbox;
        this.imageOptions.bounds = data.bounds;

        if (baseLayer && baseLayer.options) {
          this.imageOptions.basemap = baseLayer;
        }

        /* If the vizjson contains a named map and a torque layer with a named map,
           ignore the torque layer */

        var ignoreTorqueLayer = false;

        var namedMap = this._getLayerByType(data.layers, "namedmap");

        if (namedMap) {

          var torque = this._getLayerByType(data.layers, "torque");

          if (torque && torque.options && torque.options.named_map) {

            if (torque.options.named_map.name === namedMap.options.named_map.name) {
              ignoreTorqueLayer = true;
            }

          }

        }

        var layers = [ this._getBasemapLayer() ];

        for (var i = 1; i < data.layers.length; i++) {

          var layer = data.layers[i];

          if (layer.type === "torque" && !ignoreTorqueLayer) {

            layers.push(this._getTorqueLayerDefinition(layer));

          } else if (layer.type === "namedmap") {

            layers.push(this._getNamedmapLayerDefinition(layer));

          } else if (layer.type !== "torque" && layer.type !== "namedmap") {

            var ll = this._getLayergroupLayerDefinition(layer);

            for (var j = 0; j < ll.length; j++) {
              layers.push(ll[j]);
            }

          }
        }

        this.options.layers = { layers: layers };
        this._requestLayerGroupID();

      }

    },

    _getLayerByType: function(layers, type) {
      return _.find(layers, function(layer) { return layer.type === type; });
    },

    _setupTilerConfiguration: function(protocol, domain, port) {

      this.options.tiler_domain   = domain;
      this.options.tiler_protocol = protocol;
      this.options.tiler_port     = port;

      if (this.userOptions.https || this.imageOptions.vizjson.indexOf("https") === 0) {
        this.options.tiler_protocol = "https";
        this.options.tiler_port     = 443;
      }

    },

    toJSON: function(){
      return this.options.layers;
    },

    _requestLayerGroupID: function() {

      var self = this;

      this._requestPOST({}, function(data, error) {

        if (error) {
          self.error = error;
        }

        if (data) {
          self.imageOptions.layergroupid = data.layergroupid;
        }

        self.queue.flush(this);

      });

    },

    _getDefaultBasemapLayer: function() {

      return {
        type: "http",
        options: {
          urlTemplate: this.defaults.basemap_url_template,
          subdomains:  this.defaults.basemap_subdomains
        }
      };

    },

    _getHTTPBasemapLayer: function(basemap) {

      return {
        type: "http",
        options: {
          urlTemplate: basemap.options.urlTemplate,
          subdomains: basemap.options.subdomains || this.defaults.basemap_subdomains
        }
      };

    },

    _getPlainBasemapLayer: function(color) {

      return {
        type: "plain",
        options: {
          color: color
        }
      };

    },

    _getBasemapLayer: function() {

      var basemap = this.userOptions.basemap || this.imageOptions.basemap;

      if (basemap) {

        // TODO: refactor this
        var type = basemap.type.toLowerCase();

        if (basemap.options && basemap.options.type) {
          type = basemap.options.type.toLowerCase();
        }

        if (type === "plain") {
          return this._getPlainBasemapLayer(basemap.options.color);
        } else {
          return this._getHTTPBasemapLayer(basemap);
        }

      }

      return this._getDefaultBasemapLayer();

    },

    _getTorqueLayerDefinition: function(layer_definition) {

      var layerDefinition = new LayerDefinition(layer_definition, layer_definition.options);

      var query    = layerDefinition.options.query || "SELECT * FROM " + layerDefinition.options.table_name;
      var cartocss = layer_definition.options.tile_style;

      return {
        type: "torque",
        options: {
          sql: query,
          cartocss: cartocss
        }
      };

    },

    _getLayergroupLayerDefinition: function(layer) {

      var options = layer.options;
      var layerDefinition = new LayerDefinition(options.layer_definition, options);

      return layerDefinition.toJSON().layers;

    },

    _getNamedmapLayerDefinition: function(layer) {

      var options = layer.options;

      var layerDefinition = new NamedMap(options.named_map, options);

      //layerDefinition.options.type = "named";

      return { type: "named",
        options: {
          name: layerDefinition.named_map.name
        }
      }

    },

    _getUrl: function() {

      var username     = this.options.user_name;
      var bbox         = this.imageOptions.bbox;
      var layergroupid = this.imageOptions.layergroupid;
      var zoom         = this.imageOptions.zoom   || this.defaults.zoom;
      var center       = this.imageOptions.center || this.defaults.center;
      var size         = this.imageOptions.size   || this.defaults.size;
      var format       = this.imageOptions.format || this.defaults.format;

      var lat    = center[0];
      var lon    = center[1];

      var width  = size[0];
      var height = size[1];

      var url = this._tilerHost() + this.endPoint;

      if (bbox && bbox.length) {
        return [url, "static/bbox" , layergroupid, bbox.join(","), width, height + "." + format].join("/");
      } else {
        return [url, "static/center" , layergroupid, zoom, lat, lon, width, height + "." + format].join("/");
      }

    },

    // Generates a random string
    _getUUID: function() {
      var S4 = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
      };
      return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
    },

    /* Setters */
    _set: function(name, value) {

      var self = this;

      this.queue.add(function() {
        self.imageOptions[name] = value;
      });

      return this;

    },

    zoom: function(zoom) {
      return this._set("zoom", zoom);
    },

    bbox: function(bbox) {
      return this._set("bbox", bbox);
    },

    center: function(center) {
      this._set("bbox", null);
      return this._set("center", center);
    },

    format: function(format) {
      return this._set("format", _.include(this.supported_formats, format) ? format : this.defaults.format);
    },

    size: function(width, height) {
      return this._set("size", [width, height === undefined ? width : height]);
    },

    /* Methods */

    /* Image.into(HTMLImageElement)
       inserts the image in the HTMLImageElement specified */
    into: function(img) {

      var self = this;

      if (!(img instanceof HTMLImageElement)) {
        cartodb.log.error("img should be an image");
        return;
      }

      this.imageOptions.size = [img.width, img.height];

      this.queue.add(function(response) {
        img.src = self._getUrl();
      });

    },

    /* Image.getUrl(callback(err, url))
       gets the url for the image, err is null is there was no error */

    getUrl: function(callback) {

      var self = this;

      this.queue.add(function() {
        if (callback) {
          callback(self.error, self._getUrl());
        }
      });

    },

    /* Image.write(attributes)
       adds a img tag in the same place script is executed */

    write: function(attributes) {

      var self = this;

      this.imageOptions.attributes = attributes;

      if (attributes && attributes.src) {
        document.write('');
      } else {
        document.write('');
      }

      this.queue.add(function() {

        var element = document.getElementById(self.imageOptions.temp_id);

        element.src = self._getUrl();
        element.removeAttribute("temp_id");

        var attributes = self.imageOptions.attributes;

        if (attributes && attributes.class) { element.setAttribute("class", attributes.class); }
        if (attributes && attributes.id)    { element.setAttribute("id", attributes.id); }

      });

      return this;
    }

  })

  cdb.Image = function(data, options) {

    if (!options) options = {};

    var image = new Image();

    if (typeof data === 'string') {
      image.load(data, options);
    } else {
      image.loadLayerDefinition(data);
    }

    return image;

  };

})();
<|endoftext|>


<commit_before>export function canStart (state) {
  return state && (state === 'down' || state === 'paused' || state === 'suspended')
}

export function canShutdown (state) {
  return canRestart(state) || state === 'reboot_in_progress'
}

export function canRestart (state) {
  return state && (state === 'up' || state === 'migrating')
}

export function canSuspend (state) {
  return state && (state === 'up')
}

export function canConsole (state) {
  return state && (state === 'up' || state === 'powering_up' || state === 'powering_down' || state === 'paused' || state === 'migrating' || state === 'reboot_in_progress' || state === 'saving_state')
}

/*
 public enum VmStatus {
 UNASSIGNED,
 DOWN,
 UP,
 POWERING_UP,
 PAUSED,
 MIGRATING,
 UNKNOWN,
 NOT_RESPONDING,
 WAIT_FOR_LAUNCH,
 REBOOT_IN_PROGRESS,
 SAVING_STATE,
 RESTORING_STATE,
 SUSPENDED,
 IMAGE_LOCKED,
 POWERING_DOWN;
 }
 */
<commit_msg>'Shut down' and 'Suspend' allowed for a paused VM<commit_after>export function canStart (state) {
  return state && (state === 'down' || state === 'paused' || state === 'suspended')
}

export function canShutdown (state) {
  return canRestart(state) || state === 'reboot_in_progress' || state === 'paused'
}

export function canRestart (state) {
  return state && (state === 'up' || state === 'migrating')
}

export function canSuspend (state) {
  return state && (state === 'up' || state === 'paused')
}

export function canConsole (state) {
  return state && (state === 'up' || state === 'powering_up' || state === 'powering_down' || state === 'paused' || state === 'migrating' || state === 'reboot_in_progress' || state === 'saving_state')
}

/*
 public enum VmStatus {
 UNASSIGNED,
 DOWN,
 UP,
 POWERING_UP,
 PAUSED,
 MIGRATING,
 UNKNOWN,
 NOT_RESPONDING,
 WAIT_FOR_LAUNCH,
 REBOOT_IN_PROGRESS,
 SAVING_STATE,
 RESTORING_STATE,
 SUSPENDED,
 IMAGE_LOCKED,
 POWERING_DOWN;
 }
 */
<|endoftext|>


<commit_before>// d3 margin convention
var margin = {
  top: 50,
  left: 50,
  bottom: 50,
  right: 50,
}

var width = 1060 - margin.left - margin.right; // 960
var height = 800 - margin.top - margin.bottom; // 700
var centered;

var bbVis = {
  x: 100,
  y: 10,
  w: width - 100,
  h: 300
}

var detailVis = d3.select('#detailVis').append('svg')
  .attr('width', 350)
  .attr('height', 200)

var svg = d3.select('#vis').append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  //.on('click', clicked)

var g = svg.append('g')
  .attr('class', 'country')
  .attr('transform', 'translate(' + margin.left + ','
    + margin.right + ')')

var projection = d3.geo.albersUsa()
  .translate([width / 2, height / 2])
  .precision(0.1)

var path = d3.geo.path()
  .projection(projection)

var dataset = [];
var completeDataset = [];

d3.json('../data/us-named.json', function(error, data) {

  // convert topojson to geojson
  var usMap = topojson.feature(data, data.objects.states);

  g
    .selectAll('.state')
    .data(usMap.features)
    .enter()
      .append('path')
      .attr('class', 'state')
      .attr('d', path)
      .on('click', clicked)

  // needed?
//  g
//    .append('path')
//    .datum(topojson.mesh(data, data.objects.states, function(a, b) {
//      return a !== b;
//    }));

  loadStats();
}); // end d3.json -- us-named

function clicked(d) {
  var x, y, k;

  if (d && centered !== d) {
    var centroid = path.centroid(d);
    x = centroid[0];
    y = centroid[1];
    k = 4;
    centered = d;
  } else {
    x = width / 2;
    y = height / 2;
    k = 1;
    centered = null;
  }

  //console.log(x, y);
  g
    .selectAll('path')
    .classed('active', centered && function(d) {
      return d === centered;
    })

  g
    .transition()
    .duration(750)
    .attr('transform', 'translate(' + (width / 2) + ','
      + (height / 2) + ')scale(' + k + ')translate('
      + -x + ',' + -y + ')')
    .style('stroke-width', 1.5 / k + 'px')

}

function loadStations() {
  d3.csv('../data/NSRDB_StationsMeta.csv', function(error, data) {
  //console.log(data);

// for calcuating a good radius
//  var sums = [];
//  for (var s in completeDataset) {
//    sums.push(completeDataset[s].sum)
//  }
//  var max = d3.max(sums);
//  var min = d3.min(sums);
//  console.log(min, max);

  g
    .selectAll('.station')
    .data(data)
    .enter()
      .append('circle')
      .attr('class', 'station')
      .attr('cx', function(d) {
        var x = projection(
          [
            parseFloat(d['NSRDB_LON(dd)']),
            parseFloat(d['NSRDB_LAT (dd)'])
          ]
        );
        // make sure we have an array before returning part of it
        if (x) return x[0];
      })
      .attr('cy', function(d) {
        var y = projection(
          [
            parseFloat(d['NSRDB_LON(dd)']),
            parseFloat(d['NSRDB_LAT (dd)'])
          ]
        );
        // make sure we have an array before returning part of it
        if (y) return y[1];
      })
      .attr('r', function(d) {
        var usaf = d['USAF'];
        if (completeDataset[usaf]) {
          return completeDataset[usaf].sum / 10000000;
        } else {
          return 2;
        }
      })
      .style('fill', function(d) {
        var usaf = d['USAF'];
        if (!completeDataset[usaf]) {
          return '#aaa';
        }
      })
      .style('stroke', function(d) {
        var usaf = d['USAF'];
        if (!completeDataset[usaf]) {
          return '#aaa';
        }
      })

    //console.log(completeDataset['690150'].sum);

  }); // end d3.csv() -- stationsmeta
} // end loadStations()

function loadStats() {
  d3.json('../data/reduceMonthStationHour2003_2004.json',
    function(error, data) {

    completeDataset = data;

    loadStations();

  }); // end d3.json()
} // end loadStats()

function zoomIn(d) {
  console.log(d.properties.name);
}

function zoomOut() {

}
<commit_msg>tries to get tooltip working<commit_after>// d3 margin convention
var margin = {
  top: 50,
  left: 50,
  bottom: 50,
  right: 50,
}

var width = 1060 - margin.left - margin.right; // 960
var height = 800 - margin.top - margin.bottom; // 700
var centered;

var bbVis = {
  x: 100,
  y: 10,
  w: width - 100,
  h: 300
}

var detailVis = d3.select('#detailVis').append('svg')
  .attr('width', 350)
  .attr('height', 200)

var svg = d3.select('#vis').append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  //.on('click', clicked)

var g = svg.append('g')
  .attr('class', 'country')
  .attr('transform', 'translate(' + margin.left + ','
    + margin.right + ')')

var projection = d3.geo.albersUsa()
  .translate([width / 2, height / 2])
  .precision(0.1)

var path = d3.geo.path()
  .projection(projection)

var dataset = [];
var completeDataset = [];

var tip = d3.tip()
  //.attr('class', 'd3-tip')
  .attr('class', function(d) {
  })
  .offset([-10, 0])
  .html(function(d) {
    console.log(d['properties']['name']);
    //return '<strong>' + d + '</strong>';
  });

d3.json('../data/us-named.json', function(error, data) {

  // convert topojson to geojson
  var usMap = topojson.feature(data, data.objects.states);

  g
    .selectAll('.state')
    .data(usMap.features)
    .enter()
      .append('path')
      .attr('class', 'state')
      .attr('d', path)
      //.on('click', clicked)
      .on('mouseover', tip.show)
      .on('mouseout', tip.hide)

  // needed?
//  g
//    .append('path')
//    .datum(topojson.mesh(data, data.objects.states, function(a, b) {
//      return a !== b;
//    }));

  loadStats();
}); // end d3.json -- us-named

function clicked(d) {
  var x, y, k;

  if (d && centered !== d) {
    var centroid = path.centroid(d);
    x = centroid[0];
    y = centroid[1];
    k = 4;
    centered = d;
  } else {
    x = width / 2;
    y = height / 2;
    k = 1;
    centered = null;
  }

  //console.log(x, y);
  g
    .selectAll('path')
    .classed('active', centered && function(d) {
      return d === centered;
    })

  g
    .transition()
    .duration(750)
    .attr('transform', 'translate(' + (width / 2) + ','
      + (height / 2) + ')scale(' + k + ')translate('
      + -x + ',' + -y + ')')
    .style('stroke-width', 1.5 / k + 'px')

}

function loadStations() {
  d3.csv('../data/NSRDB_StationsMeta.csv', function(error, data) {
  //console.log(data);

// for calcuating a good radius
//  var sums = [];
//  for (var s in completeDataset) {
//    sums.push(completeDataset[s].sum)
//  }
//  var max = d3.max(sums);
//  var min = d3.min(sums);
//  console.log(min, max);

  g
    .selectAll('.station')
    .data(data)
    .enter()
      .append('circle')
      .attr('class', 'station')
      .attr('cx', function(d) {
        var x = projection(
          [
            parseFloat(d['NSRDB_LON(dd)']),
            parseFloat(d['NSRDB_LAT (dd)'])
          ]
        );
        // make sure we have an array before returning part of it
        if (x) return x[0];
      })
      .attr('cy', function(d) {
        var y = projection(
          [
            parseFloat(d['NSRDB_LON(dd)']),
            parseFloat(d['NSRDB_LAT (dd)'])
          ]
        );
        // make sure we have an array before returning part of it
        if (y) return y[1];
      })
      .attr('r', function(d) {
        var usaf = d['USAF'];
        if (completeDataset[usaf]) {
          return completeDataset[usaf].sum / 10000000;
        } else {
          return 2;
        }
      })
      .style('fill', function(d) {
        var usaf = d['USAF'];
        if (!completeDataset[usaf]) {
          return '#aaa';
        }
      })
      .style('stroke', function(d) {
        var usaf = d['USAF'];
        if (!completeDataset[usaf]) {
          return '#aaa';
        }
      })
      //.on('mouseover', tip.show)
      //.on('mouseout', tip.hide)

    //console.log(completeDataset['690150'].sum);

  }); // end d3.csv() -- stationsmeta
} // end loadStations()

function loadStats() {
  d3.json('../data/reduceMonthStationHour2003_2004.json',
    function(error, data) {

    completeDataset = data;

    loadStations();

  }); // end d3.json()
} // end loadStats()

function zoomIn(d) {
  console.log(d.properties.name);
}

function zoomOut() {

}
<|endoftext|>


<commit_before>// @flow

import React from 'react'
import serializeError from 'serialize-error'
import {RaisedButton} from '../../components/button'
import {
	convertStudent,
	semesterName,
	type PartialStudent,
} from '@gob/school-st-olaf-college'
import {Map, List} from 'immutable'
import {getOnlyCourse, getCourse} from '../../helpers/get-courses'
import {StudentSummary} from '../../modules/student/student-summary'
import {
	action as initStudent,
	type ActionCreator as InitStudentFunc,
} from '../../redux/students/actions/init-student'
import {connect} from 'react-redux'
import type {Course as CourseType, CourseError} from '@gob/types'
import {Header} from './components'
import './method-import.scss'

import {Student, Schedule, type FabricationType} from '@gob/object-student'

type Props = {
	+initStudent: InitStudentFunc, // redux
	+navigate?: string => mixed,
}

type State = {
	status: 'pending' | 'processing' | 'ready',
	error: ?Error,
	ids: Array<mixed>,
	selectedId: ?number,
	student: ?Student,
	rawStudentText: string,
	parsedStudentText: ?PartialStudent,
}

class SISImportScreen extends React.Component<Props, State> {
	state = {
		status: 'pending',
		error: null,
		ids: [],
		selectedId: null,
		student: null,
		rawStudentText: '',
		parsedStudentText: null,
	}

	handleImportData = async () => {
		let {parsedStudentText} = this.state
		if (!parsedStudentText) {
			this.setState(() => ({error: new Error('no data to import!')}))
			return
		}

		this.setState(() => ({status: 'processing'}))

		try {
			let student = await convertStudent(parsedStudentText, getOnlyCourse)
			this.setState(() => ({student}))
		} catch (error) {
			console.warn(error)
			this.setState(() => ({error: serializeError(error)}))
		}
	}

	handleCreateStudent = () => {
		if (!this.state.student) {
			return
		}
		let id = this.state.student.id
		this.props.initStudent(this.state.student)

		if (!this.props.navigate) {
			throw new Error('no navigate prop passed!')
		}
		this.props.navigate(`/student/${id}`)
	}

	handleRawStudent = (ev: SyntheticInputEvent<HTMLTextAreaElement>) => {
		ev.preventDefault()

		let data = ev.currentTarget.value

		this.setState(() => ({rawStudentText: data}))

		this.setState(
			() => {
				try {
					return {parsedStudentText: JSON.parse(data)}
				} catch (error) {
					console.warn(error)
					return {error}
				}
			},
			() => {
				this.handleImportData()
			},
		)
	}

	render() {
		let {student, error, parsedStudentText} = this.state

		return (
			<>

Import from the SIS

					This is a Work-In-Progress. It may not work
					at all!

					To import your student data from St. Olaf's SIS, follow the
					following steps:

						Open{' '}

							stolaf.edu/sis/st-courses-json.cfm
						{' '}
						in a new tab

Copy the text from the text box (all of it)

Paste the text into the text box below

				<textarea
					style={{width: '100%', height: '100px'}}
					value={this.state.rawStudentText}
					onChange={this.handleRawStudent}
					placeholder="Paste the gibberish here"
				/>

				{parsedStudentText && (

						Parsed student data

{JSON.stringify(parsedStudentText, null, 2)}

				)}

				{error && (

							{error.name}: {error.message}

{error.stack}

				)}

				{student && <StudentInfo student={student} />}

				{student && (
					<RaisedButton onClick={this.handleCreateStudent}>
						Import Student
					</RaisedButton>
				)}
			</>
		)
	}
}

const StudentInfo = ({student}: {student: Student}) => (
	<>
		<StudentSummary
			student={student}
			showEditor={false}
			showMessage={false}
		/>

			{student.schedules
				.groupBy(s => s.year)
				.map((schedules, year) => (

						{year}:
						<ScheduleListing
							fabrications={student.fabrications}
							schedules={schedules}
						/>

				))
				.toList()
				.toArray()}

	</>
)

const ScheduleListing = (props: {
	schedules: Map<string, Schedule>,
	fabrications: List<FabricationType>,
}) => {
	let {schedules = Map(), fabrications = List()} = props

	return (

			{schedules
				.sortBy(s => s.semester)
				.map(schedule => (

						{semesterName(schedule.semester)}:
						<AbbreviatedCourseListing
							fabrications={fabrications}
							schedule={schedule}
						/>

				))
				.toList()
				.toArray()}

	)
}

class AbbreviatedCourseListing extends React.Component<
	{schedule: Schedule, fabrications: List<FabricationType>},
	{courses: List<CourseType | FabricationType | CourseError>},
> {
	state = {courses: List()}
	componentDidMount() {
		this.fetchCourses()
	}
	fetchCourses = async () => {
		let courses = await this.props.schedule.getCourses(
			getCourse,
			this.props.fabrications,
		)
		this.setState(() => ({courses}))
	}
	render() {
		let {courses = List()} = this.state

		return (

				{courses
					// TODO: replace any with handling of CourseError
					.map((course: any) => (

							{course.department} {course.number}
							{course.section} – {course.name}

					))
					.toArray()}

		)
	}
}

export default connect(
	undefined,
	{initStudent},
)(SISImportScreen)
<commit_msg>sort years/schedules when importing from SIS<commit_after>// @flow

import React from 'react'
import serializeError from 'serialize-error'
import {RaisedButton} from '../../components/button'
import {
	convertStudent,
	semesterName,
	type PartialStudent,
} from '@gob/school-st-olaf-college'
import {Map, List} from 'immutable'
import {getOnlyCourse, getCourse} from '../../helpers/get-courses'
import {StudentSummary} from '../../modules/student/student-summary'
import {
	action as initStudent,
	type ActionCreator as InitStudentFunc,
} from '../../redux/students/actions/init-student'
import {connect} from 'react-redux'
import type {Course as CourseType, CourseError} from '@gob/types'
import {Header} from './components'
import './method-import.scss'

import {Student, Schedule, type FabricationType} from '@gob/object-student'

type Props = {
	+initStudent: InitStudentFunc, // redux
	+navigate?: string => mixed,
}

type State = {
	status: 'pending' | 'processing' | 'ready',
	error: ?Error,
	ids: Array<mixed>,
	selectedId: ?number,
	student: ?Student,
	rawStudentText: string,
	parsedStudentText: ?PartialStudent,
}

class SISImportScreen extends React.Component<Props, State> {
	state = {
		status: 'pending',
		error: null,
		ids: [],
		selectedId: null,
		student: null,
		rawStudentText: '',
		parsedStudentText: null,
	}

	handleImportData = async () => {
		let {parsedStudentText} = this.state
		if (!parsedStudentText) {
			this.setState(() => ({error: new Error('no data to import!')}))
			return
		}

		this.setState(() => ({status: 'processing'}))

		try {
			let student = await convertStudent(parsedStudentText, getOnlyCourse)
			this.setState(() => ({student}))
		} catch (error) {
			console.warn(error)
			this.setState(() => ({error: serializeError(error)}))
		}
	}

	handleCreateStudent = () => {
		if (!this.state.student) {
			return
		}
		let id = this.state.student.id
		this.props.initStudent(this.state.student)

		if (!this.props.navigate) {
			throw new Error('no navigate prop passed!')
		}
		this.props.navigate(`/student/${id}`)
	}

	handleRawStudent = (ev: SyntheticInputEvent<HTMLTextAreaElement>) => {
		ev.preventDefault()

		let data = ev.currentTarget.value

		this.setState(() => ({rawStudentText: data}))

		this.setState(
			() => {
				try {
					return {parsedStudentText: JSON.parse(data)}
				} catch (error) {
					console.warn(error)
					return {error}
				}
			},
			() => {
				this.handleImportData()
			},
		)
	}

	render() {
		let {student, error, parsedStudentText} = this.state

		return (
			<>

Import from the SIS

					This is a Work-In-Progress. It may not work
					at all!

					To import your student data from St. Olaf's SIS, follow the
					following steps:

						Open{' '}

							stolaf.edu/sis/st-courses-json.cfm
						{' '}
						in a new tab

Copy the text from the text box (all of it)

Paste the text into the text box below

				<textarea
					style={{width: '100%', height: '100px'}}
					value={this.state.rawStudentText}
					onChange={this.handleRawStudent}
					placeholder="Paste the gibberish here"
				/>

				{parsedStudentText && (

						Parsed student data

{JSON.stringify(parsedStudentText, null, 2)}

				)}

				{error && (

							{error.name}: {error.message}

{error.stack}

				)}

				{student && <StudentInfo student={student} />}

				{student && (
					<RaisedButton onClick={this.handleCreateStudent}>
						Import Student
					</RaisedButton>
				)}
			</>
		)
	}
}

const StudentInfo = ({student}: {student: Student}) => (
	<>
		<StudentSummary
			student={student}
			showEditor={false}
			showMessage={false}
		/>

			{student.schedules
				.sortBy(s => s.getTerm())
				.groupBy(s => s.year)
				.map((schedules, year) => (

						{year}:
						<ScheduleListing
							fabrications={student.fabrications}
							schedules={schedules}
						/>

				))
				.toList()
				.toArray()}

	</>
)

const ScheduleListing = (props: {
	schedules: Map<string, Schedule>,
	fabrications: List<FabricationType>,
}) => {
	let {schedules = Map(), fabrications = List()} = props

	return (

			{schedules
				.sortBy(s => s.semester)
				.map(schedule => (

						{semesterName(schedule.semester)}:
						<AbbreviatedCourseListing
							fabrications={fabrications}
							schedule={schedule}
						/>

				))
				.toList()
				.toArray()}

	)
}

class AbbreviatedCourseListing extends React.Component<
	{schedule: Schedule, fabrications: List<FabricationType>},
	{courses: List<CourseType | FabricationType | CourseError>},
> {
	state = {courses: List()}
	componentDidMount() {
		this.fetchCourses()
	}
	fetchCourses = async () => {
		let courses = await this.props.schedule.getCourses(
			getCourse,
			this.props.fabrications,
		)
		this.setState(() => ({courses}))
	}
	render() {
		let {courses = List()} = this.state

		return (

				{courses
					// TODO: replace any with handling of CourseError
					.map((course: any) => (

							{course.department} {course.number}
							{course.section} – {course.name}

					))
					.toArray()}

		)
	}
}

export default connect(
	undefined,
	{initStudent},
)(SISImportScreen)
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 "OKso http://okso.me"

This file is part of Skink.

Intercom is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

window.onload = function() {
    "use strict";

    var websocket = "ws://" + window.location.host + "/realtime/?" + location.pathname;
    var ws;
    if (window.WebSocket) {
        ws = new WebSocket(websocket);
    }
    else if (window.MozWebSocket) {
        ws = new MozWebSocket(websocket);
    }
    else {
        alert("WebSocket Not Supported");
        return;
    }

    window.onbeforeunload = function(e) {
        console.log("Bye bye...");
        ws.close(1000, "%(username)s left the room");

        if (!e) e = window.event;
        e.stopPropagation();
        e.preventDefault();
    };
    ws.onmessage = function (evt) {
        console.log("received message [", evt.data + "]");

        // Execute a void command:
        if (evt.data.indexOf("$") === 0) {
            var command = evt.data.substr(1);
            //console.log('exec of [ ' + command + ' ]');
            eval(command);
        }

        // Execute a command and return the synchronously:
        else if (evt.data.indexOf("?") === 0) {
            var command = evt.data.substr(1).split("=", 2);
            console.log("raw command = [ " + command + " ]");
            var callback_id = command[0];
            var command = command[1];
            console.log("callback_id = [ " + callback_id + " ]");
            console.log("processed command = [ " + command + " ]");
            try {
                var result = eval(command);
                console.log("result = [" + result + "]");
                //ws.send("?" + callback_id + "=" + typeof(result) + ":" + result);
                ws.send(JSON.stringify({
                    "action": "eval",
                    "callback": callback_id,
                    "value": result
                    }));
            }
            catch(err) {
                console.log("error : " + err.name + err.message);
                ws.send(JSON.stringify({
                    "action": "exception",
                    "callback": callback_id,
                    "name": err.name,
                    "description": err.message
                    }))
            }
        }
        console.log("log" + evt.data);
    };
    ws.onopen = function() {
        ws.send(JSON.stringify({
            "action": "info",
            "message": "%(username)s entered the room"
        }))
    };
    ws.onclose = function(evt) {
        document.getElementById("stderr").innerHTML = "Connection closed by server: " + evt.code + " \"" + evt.reason + "\"\n";
    };

    window.skink = {
        ws: ws,
        call: function(name, args=[]) {
            this.ws.send(JSON.stringify({
                action: "callback",
                callback: name,
                args: args
            }))
        }
    };
};<commit_msg>[fix] Auto reconnect from client websocket<commit_after>/*
Copyright (c) 2014 "OKso http://okso.me"

This file is part of Skink.

Intercom is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

setup_skink_websocket = function() {
    "use strict";

    var websocket = "ws://" + window.location.host + "/realtime/?" + location.pathname;
    var ws;
    if (window.WebSocket) {
        ws = new WebSocket(websocket);
    }
    else if (window.MozWebSocket) {
        ws = new MozWebSocket(websocket);
    }
    else {
        alert("WebSocket Not Supported");
        return;
    }

    window.onbeforeunload = function(e) {
        console.log("Bye bye...");
        ws.close(1000, "%(username)s left the room");

        if (!e) e = window.event;
        e.stopPropagation();
        e.preventDefault();
    };
    ws.onmessage = function (evt) {
        console.log("received message [", evt.data + "]");

        // Execute a void command:
        if (evt.data.indexOf("$") === 0) {
            var command = evt.data.substr(1);
            //console.log('exec of [ ' + command + ' ]');
            eval(command);
        }

        // Execute a command and return the synchronously:
        else if (evt.data.indexOf("?") === 0) {
            var command = evt.data.substr(1).split("=", 2);
            console.log("raw command = [ " + command + " ]");
            var callback_id = command[0];
            var command = command[1];
            console.log("callback_id = [ " + callback_id + " ]");
            console.log("processed command = [ " + command + " ]");
            try {
                var result = eval(command);
                console.log("result = [" + result + "]");
                //ws.send("?" + callback_id + "=" + typeof(result) + ":" + result);
                ws.send(JSON.stringify({
                    "action": "eval",
                    "callback": callback_id,
                    "value": result
                    }));
            }
            catch(err) {
                console.log("error : " + err.name + err.message);
                ws.send(JSON.stringify({
                    "action": "exception",
                    "callback": callback_id,
                    "name": err.name,
                    "description": err.message
                    }))
            }
        }
        console.log("log" + evt.data);
    };
    ws.onopen = function() {
        ws.send(JSON.stringify({
            "action": "info",
            "message": "%(username)s entered the room"
        }))
    };
    ws.onclose = function(evt) {
        document.getElementById("stderr").innerHTML = "Connection closed by server: " + evt.code + " \"" + evt.reason + "\"\n";

         setTimeout(function () {
            // Connection has closed so try to reconnect every 3 seconds.
            setup_skink_websocket();
        }, 3*1000);
    };

    window.skink = {
        ws: ws,
        call: function(name, args=[]) {
            this.ws.send(JSON.stringify({
                action: "callback",
                callback: name,
                args: args
            }))
        }
    };
};

window.onload = setup_skink_websocket()
<|endoftext|>


<commit_before>var _ = require('lodash');
var constants = require('../public/shared/gj_constants');

var GameObject = require('./game_object');
var Bloodsplosion = require('./bloodsplosion');
var Axe = require('./axe');
var Lazer = require('./lazer');

var SPAWN_COUNTDOWN = 30;
var INVULNERABLE_COUNTDOWN = 30;

var Player = function(id, color) {

    Player.superclass.call(this, id);

    this.type = 'player';
    this.color = color;
    this.x = 400;
    this.y = 320;
    this.w = 16;
    this.h = 24;

    this.kills = 0;
    this.deaths = 0;
    this.facex = 1;

    this.dead = false;

    // Sound hooks
    this.jumped = false;
    this.justDied = false;

    this.spawnCountdown = SPAWN_COUNTDOWN;
    this.invulnerableCountdown = INVULNERABLE_COUNTDOWN;

    this._keysDown = {};
    this._prevKeysDown = {};
    this._mousePos = { x: null, y: null };
    this._mouseDown = false;
    this._mouseWasDown = false;
    this._standing = 0; // When greater than zero, the player is on the ground.  Counts down every frame. Reset every time a ground collision is detected.
    this._roofing = 0; // same as standing but for roof
    this._droppingKick = false;

    this._cachedMap = null;
    this._fireDelay = 400;
    this._lastFireTime = null;
};

Player.superclass = GameObject;

_.extend(
    Player.prototype,
    GameObject.prototype,
    {
        update: function(state) {
            this._cachedMap = state.maps[state.era];

            this.jumped = false;

            if (this.justDied) {
                var emitter = new Bloodsplosion(this.x, this.y - this.h / 2);
                state.addObject(emitter);
                this.justDied = false;
                this.dead = true;
                this.vx = this.vy = 0;
                this.spawnCountdown = SPAWN_COUNTDOWN;
                this.invulnerableCountdown = INVULNERABLE_COUNTDOWN;
                return;
            }

            if (this.spawnCountdown > 0) {
                if (--this.spawnCountdown <= 0) {
                    this.dead = false;
                    this.moveToSpawnPoint();
                } else return;
            }

            if (this.invulnerableCountdown > 0) {
                --this.invulnerableCountdown;
            }

            if (this._keysDown[constants.keys.MOVE_LEFT]) {
                this.facex = -1;
            } else if (this._keysDown[constants.keys.MOVE_RIGHT]) {
                this.facex = 1;
            }

            if (this._standing > 0) this._standing--;
            if (this._roofing > 0) this._roofing--;

            switch (state.era) {
                case constants.eras.JUNGLE:
                    this.moveSelf_jungle(state);
                    break;
                case constants.eras.TUNDRA:
                    this.moveSelf_tundra(state);
                    break;
                case constants.eras.FUTURE:
                    this.moveSelf_future(state);
                    break;
            }

            if (this._mouseDown) {
                if (!this._lastFireTime || (new Date().getTime() - this._lastFireTime) > this._fireDelay) {
                    this._lastFireTime = new Date().getTime();
                    this.fireWeapon(state);
                }
            }

            // Integrate velocity in to position and collide with the map.
            this.x += this.vx/2;
            this.y += this.vy/2;
            this.collideWithMap(state.maps[state.era], this.takeDamage.bind(this,null));
            this.x += this.vx/2;
            this.y += this.vy/2;
            this.collideWithMap(state.maps[state.era], this.takeDamage.bind(this,null));

            this._prevKeysDown = _.clone(this._keysDown);
        },

        collideWithObject: function(other) {
            if (this._droppingKick && other.takeDamage) {
                if (this.y > other.y || !other._droppingKick) {
                    other.takeDamage(this);
                }
            }
        },

        _moveX: function (turnx, accx, decayx, maxx) {
            if (this._keysDown[constants.keys.MOVE_LEFT]) {
                this.vx += this.vx > 0 ? -turnx : -accx;
            } else if (this._keysDown[constants.keys.MOVE_RIGHT]) {
                this.vx += this.vx < 0 ? turnx : accx;
            } else {
                this.vx *= decayx;
            }
            if (this.vx > maxx) {
                this.vx = maxx;
            } else if (this.vx < -maxx) {
                this.vx = -maxx;
            }
        },

        moveSelf_future: function(state) {
            var JET_UP = 1.2;
            var JET_SAVE = 1.6;

            if (this._standing) {
                this._moveX(2, 0.5, 0.8, 4);// Ground
            } else {
                this._moveX(0.5, 0.3, 1, 6);// Air
            }

            if (this._keysDown[constants.keys.JUMP]) {
                this._standing = 0;
                this.vy -= this.vy >= 0 ? JET_SAVE : JET_UP;
                this.vy -= 0.3;
                this.jumped = true;
            }
            this.vy += 0.8;

            if (this._roofing > 0) {
                this.vy = 4;
            }

            if (this.vy > 12) {
                this.vy = 12;
            }
            else if (this.vy < -6) {
                this.vy = -6;
            }
        },

        moveSelf_basic: function(state) {
            if (this._standing) {
                this._moveX(2, 0.5, 0.8, 6);// Ground
            } else {
                this._moveX(1, 0.3, 0.9, 6);// Air
            }

            if (this._keysDown[constants.keys.JUMP]) {
                if (this._standing > 0) {
                    this._standing = 0;
                    this.vy = -10;
                    this.jumped = true;
                }
                if (this.vy < 0) {
                    this.vy -= 0.5;
                    this.y -= 0.3;
                }
            }

            this.vy += 1.0;

            if (this.vy > 12) {
                this.vy = 12;
            }
        },

        _startKick: function() {
            var theta = Math.atan2(this._mousePos.y - (this.y - this.h/2), this._mousePos.x - this.x);
            if (theta > 0) {
                if      (theta <   Math.PI/4) theta =   Math.PI/4;
                else if (theta > 3*Math.PI/4) theta = 3*Math.PI/4;
            } else {
                if (theta > -Math.PI/2) theta =   Math.PI/4;
                else                    theta = 3*Math.PI/4;
            }
            this.vx = 15 * Math.cos (theta);
            this.vy = 15 * Math.sin (theta);
        },

        moveSelf_jungle: function(state) {
            if (this._standing) {
                this._droppingKick = false;
            } else if (!this._mouseWasDown && this._mouseDown && !this._droppingKick) {
                this._droppingKick = true;
                this._startKick();
            }

            if (!this._droppingKick) {
                this.moveSelf_basic(state);
            }
        },

        moveSelf_tundra: function(state) {
            this.moveSelf_basic(state);
        },

        fireWeapon: function(state) {
            var projectile;

            switch (state.era) {
                case constants.eras.TUNDRA:
                    projectile = Axe;
                    break;
                case constants.eras.FUTURE:
                    projectile = Lazer;
                    break;
            }

            if (projectile) {
                projectile = new projectile(this, { x: this._mousePos.x, y: this._mousePos.y });

                state.addObject(projectile);
            }
        },

        moveToSpawnPoint: function() {
            var map = this._cachedMap;
            var spawnPoint = _.sample(map._spawnPoints);
            this.x = constants.TILE_SIZE * spawnPoint[0];
            this.y = constants.TILE_SIZE * spawnPoint[1];
        },

        takeDamage: function(other) {
            if (this.justDied || this.dead) return;
            if (other && other._owner) {
                other._owner.kills++;
            }
            this.deaths++;
            this.justDied = true;
        },

        handleInput: function(input) {
            switch (input.type) {
                case 'keydown':
                    this._keysDown[input.key] = true;
                    break;
                case 'keyup':
                    delete this._keysDown[input.key];
                    break;
                case 'mousemove':
                    this._mousePos.x = input.x;
                    this._mousePos.y = input.y;
                    break;
                case 'mousedown':
                    this._mouseWasDown = this._mouseDown;
                    this._mouseDown = true;
                    break;
                case 'mouseup':
                    this._mouseDown = false;
                    break;
            }
        },

        collideWithMap: function(map, damaged) {
            map._sampledFatalTile = false;

            if (map.sampleAtPixel(this.x - this.w / 2, this.y - this.h / 2)
                || map.sampleAtPixel(this.x - this.w / 2, this.y - 2 - (this.vy > 0 ? this.vy : 0))
                || map.sampleAtPixel(this.x - this.w / 2, this.y - this.h + 2)) {
                this.x = constants.TILE_SIZE / 2 + constants.TILE_SIZE * Math.floor(this.x / constants.TILE_SIZE);
                if (this._droppingKick) {
                    this._droppingKick = false;
                    this.vx = Math.abs(this.vx)/2;
                } else {
                    this.vx = 0;
                }
            }
            if (map.sampleAtPixel(this.x + this.w / 2, this.y - this.h / 2)
                || map.sampleAtPixel(this.x + this.w / 2, this.y - 2 - (this.vy > 0 ? this.vy : 0))
                || map.sampleAtPixel(this.x + this.w / 2, this.y - this.h + 2)) {
                this.x = constants.TILE_SIZE / 2 + constants.TILE_SIZE * Math.floor(this.x / constants.TILE_SIZE);
                if (this._droppingKick) {
                    this._droppingKick = false;
                    this.vx = -Math.abs(this.vx)/2;
                } else {
                    this.vx = 0;
                }
            }
            if (map.sampleAtPixel(this.x + this.w / 2 - 2, this.y, this.vy > 0)
                || map.sampleAtPixel(this.x - this.w / 2 + 2, this.y, this.vy > 0)) {
                this.y = constants.TILE_SIZE * Math.floor(this.y / constants.TILE_SIZE);
                this.vy = 0;
                this._standing = 2;
            }
            if (map.sampleAtPixel(this.x + this.w / 2 - 2, this.y - this.h)
                || map.sampleAtPixel(this.x - this.w / 2 + 2, this.y - this.h)) {
                this.y = constants.TILE_SIZE * Math.ceil(this.y / constants.TILE_SIZE);
                if (this.vy < 0) this.vy = 0;
                this._roofing = 2;
            }

            // TODO make sure taking damage here doesnt allow double death
            if (map._sampledFatalTile && damaged && !this.invulnerableCountdown) damaged();
        }
    }
);

module.exports = Player;
<commit_msg>fixed jumped flag in future<commit_after>var _ = require('lodash');
var constants = require('../public/shared/gj_constants');

var GameObject = require('./game_object');
var Bloodsplosion = require('./bloodsplosion');
var Axe = require('./axe');
var Lazer = require('./lazer');

var SPAWN_COUNTDOWN = 30;
var INVULNERABLE_COUNTDOWN = 30;

var Player = function(id, color) {

    Player.superclass.call(this, id);

    this.type = 'player';
    this.color = color;
    this.x = 400;
    this.y = 320;
    this.w = 16;
    this.h = 24;

    this.kills = 0;
    this.deaths = 0;
    this.facex = 1;

    this.dead = false;

    // Sound hooks
    this.jumped = false;
    this.justDied = false;

    this.spawnCountdown = SPAWN_COUNTDOWN;
    this.invulnerableCountdown = INVULNERABLE_COUNTDOWN;

    this._keysDown = {};
    this._prevKeysDown = {};
    this._mousePos = { x: null, y: null };
    this._mouseDown = false;
    this._mouseWasDown = false;
    this._standing = 0; // When greater than zero, the player is on the ground.  Counts down every frame. Reset every time a ground collision is detected.
    this._roofing = 0; // same as standing but for roof
    this._droppingKick = false;

    this._cachedMap = null;
    this._fireDelay = 400;
    this._lastFireTime = null;
};

Player.superclass = GameObject;

_.extend(
    Player.prototype,
    GameObject.prototype,
    {
        update: function(state) {
            this._cachedMap = state.maps[state.era];

            this.jumped = false;

            if (this.justDied) {
                var emitter = new Bloodsplosion(this.x, this.y - this.h / 2);
                state.addObject(emitter);
                this.justDied = false;
                this.dead = true;
                this.vx = this.vy = 0;
                this.spawnCountdown = SPAWN_COUNTDOWN;
                this.invulnerableCountdown = INVULNERABLE_COUNTDOWN;
                return;
            }

            if (this.spawnCountdown > 0) {
                if (--this.spawnCountdown <= 0) {
                    this.dead = false;
                    this.moveToSpawnPoint();
                } else return;
            }

            if (this.invulnerableCountdown > 0) {
                --this.invulnerableCountdown;
            }

            if (this._keysDown[constants.keys.MOVE_LEFT]) {
                this.facex = -1;
            } else if (this._keysDown[constants.keys.MOVE_RIGHT]) {
                this.facex = 1;
            }

            if (this._standing > 0) this._standing--;
            if (this._roofing > 0) this._roofing--;

            switch (state.era) {
                case constants.eras.JUNGLE:
                    this.moveSelf_jungle(state);
                    break;
                case constants.eras.TUNDRA:
                    this.moveSelf_tundra(state);
                    break;
                case constants.eras.FUTURE:
                    this.moveSelf_future(state);
                    break;
            }

            if (this._mouseDown) {
                if (!this._lastFireTime || (new Date().getTime() - this._lastFireTime) > this._fireDelay) {
                    this._lastFireTime = new Date().getTime();
                    this.fireWeapon(state);
                }
            }

            // Integrate velocity in to position and collide with the map.
            this.x += this.vx/2;
            this.y += this.vy/2;
            this.collideWithMap(state.maps[state.era], this.takeDamage.bind(this,null));
            this.x += this.vx/2;
            this.y += this.vy/2;
            this.collideWithMap(state.maps[state.era], this.takeDamage.bind(this,null));

            this._prevKeysDown = _.clone(this._keysDown);
        },

        collideWithObject: function(other) {
            if (this._droppingKick && other.takeDamage) {
                if (this.y > other.y || !other._droppingKick) {
                    other.takeDamage(this);
                }
            }
        },

        _moveX: function (turnx, accx, decayx, maxx) {
            if (this._keysDown[constants.keys.MOVE_LEFT]) {
                this.vx += this.vx > 0 ? -turnx : -accx;
            } else if (this._keysDown[constants.keys.MOVE_RIGHT]) {
                this.vx += this.vx < 0 ? turnx : accx;
            } else {
                this.vx *= decayx;
            }
            if (this.vx > maxx) {
                this.vx = maxx;
            } else if (this.vx < -maxx) {
                this.vx = -maxx;
            }
        },

        moveSelf_future: function(state) {
            var JET_UP = 1.2;
            var JET_SAVE = 1.6;

            if (this._standing) {
                this._moveX(2, 0.5, 0.8, 4);// Ground
            } else {
                this._moveX(0.5, 0.3, 1, 6);// Air
            }

            if (this._keysDown[constants.keys.JUMP]) {
                if (this._standing) this.jumped = true;
                this._standing = 0;
                this.vy -= this.vy >= 0 ? JET_SAVE : JET_UP;
                this.vy -= 0.3;
            }
            this.vy += 0.8;

            if (this._roofing > 0) {
                this.vy = 4;
            }

            if (this.vy > 12) {
                this.vy = 12;
            }
            else if (this.vy < -6) {
                this.vy = -6;
            }
        },

        moveSelf_basic: function(state) {
            if (this._standing) {
                this._moveX(2, 0.5, 0.8, 6);// Ground
            } else {
                this._moveX(1, 0.3, 0.9, 6);// Air
            }

            if (this._keysDown[constants.keys.JUMP]) {
                if (this._standing > 0) {
                    this._standing = 0;
                    this.vy = -10;
                    this.jumped = true;
                }
                if (this.vy < 0) {
                    this.vy -= 0.5;
                    this.y -= 0.3;
                }
            }

            this.vy += 1.0;

            if (this.vy > 12) {
                this.vy = 12;
            }
        },

        _startKick: function() {
            var theta = Math.atan2(this._mousePos.y - (this.y - this.h/2), this._mousePos.x - this.x);
            if (theta > 0) {
                if      (theta <   Math.PI/4) theta =   Math.PI/4;
                else if (theta > 3*Math.PI/4) theta = 3*Math.PI/4;
            } else {
                if (theta > -Math.PI/2) theta =   Math.PI/4;
                else                    theta = 3*Math.PI/4;
            }
            this.vx = 15 * Math.cos (theta);
            this.vy = 15 * Math.sin (theta);
        },

        moveSelf_jungle: function(state) {
            if (this._standing) {
                this._droppingKick = false;
            } else if (!this._mouseWasDown && this._mouseDown && !this._droppingKick) {
                this._droppingKick = true;
                this._startKick();
            }

            if (!this._droppingKick) {
                this.moveSelf_basic(state);
            }
        },

        moveSelf_tundra: function(state) {
            this.moveSelf_basic(state);
        },

        fireWeapon: function(state) {
            var projectile;

            switch (state.era) {
                case constants.eras.TUNDRA:
                    projectile = Axe;
                    break;
                case constants.eras.FUTURE:
                    projectile = Lazer;
                    break;
            }

            if (projectile) {
                projectile = new projectile(this, { x: this._mousePos.x, y: this._mousePos.y });

                state.addObject(projectile);
            }
        },

        moveToSpawnPoint: function() {
            var map = this._cachedMap;
            var spawnPoint = _.sample(map._spawnPoints);
            this.x = constants.TILE_SIZE * spawnPoint[0];
            this.y = constants.TILE_SIZE * spawnPoint[1];
        },

        takeDamage: function(other) {
            if (this.justDied || this.dead) return;
            if (other && other._owner) {
                other._owner.kills++;
            }
            this.deaths++;
            this.justDied = true;
        },

        handleInput: function(input) {
            switch (input.type) {
                case 'keydown':
                    this._keysDown[input.key] = true;
                    break;
                case 'keyup':
                    delete this._keysDown[input.key];
                    break;
                case 'mousemove':
                    this._mousePos.x = input.x;
                    this._mousePos.y = input.y;
                    break;
                case 'mousedown':
                    this._mouseWasDown = this._mouseDown;
                    this._mouseDown = true;
                    break;
                case 'mouseup':
                    this._mouseDown = false;
                    break;
            }
        },

        collideWithMap: function(map, damaged) {
            map._sampledFatalTile = false;

            if (map.sampleAtPixel(this.x - this.w / 2, this.y - this.h / 2)
                || map.sampleAtPixel(this.x - this.w / 2, this.y - 2 - (this.vy > 0 ? this.vy : 0))
                || map.sampleAtPixel(this.x - this.w / 2, this.y - this.h + 2)) {
                this.x = constants.TILE_SIZE / 2 + constants.TILE_SIZE * Math.floor(this.x / constants.TILE_SIZE);
                if (this._droppingKick) {
                    this._droppingKick = false;
                    this.vx = Math.abs(this.vx)/2;
                } else {
                    this.vx = 0;
                }
            }
            if (map.sampleAtPixel(this.x + this.w / 2, this.y - this.h / 2)
                || map.sampleAtPixel(this.x + this.w / 2, this.y - 2 - (this.vy > 0 ? this.vy : 0))
                || map.sampleAtPixel(this.x + this.w / 2, this.y - this.h + 2)) {
                this.x = constants.TILE_SIZE / 2 + constants.TILE_SIZE * Math.floor(this.x / constants.TILE_SIZE);
                if (this._droppingKick) {
                    this._droppingKick = false;
                    this.vx = -Math.abs(this.vx)/2;
                } else {
                    this.vx = 0;
                }
            }
            if (map.sampleAtPixel(this.x + this.w / 2 - 2, this.y, this.vy > 0)
                || map.sampleAtPixel(this.x - this.w / 2 + 2, this.y, this.vy > 0)) {
                this.y = constants.TILE_SIZE * Math.floor(this.y / constants.TILE_SIZE);
                this.vy = 0;
                this._standing = 2;
            }
            if (map.sampleAtPixel(this.x + this.w / 2 - 2, this.y - this.h)
                || map.sampleAtPixel(this.x - this.w / 2 + 2, this.y - this.h)) {
                this.y = constants.TILE_SIZE * Math.ceil(this.y / constants.TILE_SIZE);
                if (this.vy < 0) this.vy = 0;
                this._roofing = 2;
            }

            // TODO make sure taking damage here doesnt allow double death
            if (map._sampledFatalTile && damaged && !this.invulnerableCountdown) damaged();
        }
    }
);

module.exports = Player;
<|endoftext|>


<commit_before>var GLContext = require("./base/context");
var GLScene = require("./scene/glscene");
var GLScaledRenderTarget = require("./base/rendertarget.js").GLScaledRenderTarget;
var DataChangeListener = require("../renderer/tools/datachangelistener.js");
var RenderInterface = require("./render-interface");
var PickObjectRenderPass= require("./render-passes/pick-object.js");
var PickPositionRenderPass = require("./render-passes/pick-position.js");
var PickNormalRenderPass = require("./render-passes/pick-normal.js");
var ForwardRenderTree = require("./render-trees/forward.js");
var GLU = require("../../contrib/glu.js");
var MAX_PICK_BUFFER_DIMENSION = 512;

var OPTION_SSAO = "renderer-ssao";
var FLAGS = {};
FLAGS[OPTION_SSAO] = {defaultValue: false, recompileOnChange: true};
for (var flag in FLAGS) {
    XML3D.options.register(flag, FLAGS[flag].defaultValue);
}

/**
 * Convert the given y-coordinate on the canvas to a y-coordinate appropriate in
 * the GL context. The y-coordinate gets turned upside-down. The lowest possible
 * canvas coordinate is 0, so we need to subtract 1 from the height, too.
 *
 * @param {HTMLCanvasElement} canvas
 * @param {number} y
 * @return {number} the converted y-coordinate
 */
var canvasToGlY = function (canvas, y) {
    return canvas.height - y - 1;
};
/**
 * @interface
 */
var IRenderer = function () {
};

IRenderer.prototype.renderToCanvas = function () {
};
IRenderer.prototype.handleResizeEvent = function (width, height) {
};
IRenderer.prototype.requestRedraw = function (reason) {
};
IRenderer.prototype.needsRedraw = function () {
};
IRenderer.prototype.getWorldSpaceNormalByPoint = function (obj, x, y) {
};
IRenderer.prototype.getWorldSpacePositionByPoint = function (obj, x, y) {
};
IRenderer.prototype.getRenderObjectFromPickingBuffer = function (x, y) {
};
IRenderer.prototype.generateRay = function (x, y) {
};
IRenderer.prototype.dispose = function () {
};

/**
 * @param {Element} element The <xml3d> Element
 * @implements {IRenderer}
 * @constructor
 */
var GLRenderer = function (element, canvasHandler) {

    this._canvasHandler = canvasHandler;
    var canvas = this._canvasHandler.getCanvas();
    this.width = canvas.clientWidth;
    this.height = canvas.clientHeight;

    this.context = new GLContext(canvas, this._canvasHandler.id);
    this.scene = new GLScene(this.context);

    var factory = XML3D.base.xml3dFormatHandler.getFactory(XML3D.webgl, this._canvasHandler.id);
    factory.setScene(this.scene);
    factory.setRenderer(this);

    var xml3dAdapter = factory.getAdapter(element);
    xml3dAdapter.traverse(function () {
    });

    this.scene.rootNode.setVisible(true);

    /** @type {RenderObject} */
    this.pickedObject = null;

    this.needsDraw = true;
    this.needsPickingDraw = true;
    this.context.requestRedraw = this.requestRedraw.bind(this);

    //Currently used as a helper to calculate view and projection matrices for ray casting, since the scene
    //must be rendered from the point of view of the ray
    this.rayCamera = this.scene.createRenderView();

    this.initGL();
    this.changeListener = new DataChangeListener(this);

    this.renderInterface = this.createRenderInterface();
    this.createDefaultPipelines();
    XML3D.options.addObserver(this.onFlagsChange.bind(this));
};

// Just to satisfy jslint
GLRenderer.prototype.generateRay = function () {
};

XML3D.extend(GLRenderer.prototype, {
    initGL: function () {
        var gl = this.context.gl;

        gl.clearColor(0, 0, 0, 0);
        gl.clearDepth(1);
        gl.clearStencil(0);

        gl.enable(gl.DEPTH_TEST);
        gl.depthFunc(gl.LEQUAL);

        gl.frontFace(gl.CCW);
        gl.cullFace(gl.BACK);
        gl.disable(gl.CULL_FACE);

        gl.blendEquation(gl.FUNC_ADD);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        gl.disable(gl.BLEND);

        gl.viewport(0, 0, this.width, this.height);

        gl.pixelStorei(gl.PACK_ALIGNMENT, 1);
        gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
        gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL);

    },

    handleResizeEvent: function (width, height) {
        this.width = width;
        this.height = height;
        this.context.handleResizeEvent(width, height);
        this.createDefaultPipelines();
        this.scene.handleResizeEvent(width, height);
        this.needsDraw = this.needsPickingDraw = true;
    },

    createDefaultPipelines: function () {
        var pipeline = new ForwardRenderTree(this.renderInterface, XML3D.options.getValue(OPTION_SSAO));
        this.renderInterface.setRenderPipeline(pipeline);

        var pickTarget = new GLScaledRenderTarget(this.context, MAX_PICK_BUFFER_DIMENSION, {
            width: this.context.canvasTarget.width,
            height: this.context.canvasTarget.height,
            colorFormat: this.context.gl.RGBA,
            depthFormat: this.context.gl.DEPTH_COMPONENT16,
            stencilFormat: null,
            depthAsRenderbuffer: true
        });
        this.pickObjectPass = new PickObjectRenderPass(this.renderInterface, pickTarget);
        this.pickPositionPass = new PickPositionRenderPass(this.renderInterface, pickTarget);
        this.pickNormalPass = new PickNormalRenderPass(this.renderInterface, pickTarget);
    },

    createRenderInterface: function () {
        return new RenderInterface(this.context, this.scene);
        //TODO need to provide an interface for creating shaders, buffers and so on
    },

    requestRedraw: function (reason) {
        XML3D.debug.logDebug("Request redraw because:", reason);
        this.needsDraw = true;
        this.needsPickingDraw = true;
    },

    getWorldSpaceNormalByPoint: function (x, y, object) {
        var obj = object || this.pickedObject;
        if (!obj)
            return null;
        y = canvasToGlY(this._canvasHandler.getCanvas(), y);
        this.pickNormalPass.render(obj);
        this.needsPickingDraw = true;
        return this.pickNormalPass.readNormalFromPickingBuffer(x, y);
    },

    getWorldSpacePositionByPoint: function (x, y, object) {
        var obj = object || this.pickedObject;
        if (!obj)
            return null;
        y = canvasToGlY(this._canvasHandler.getCanvas(), y);
        this.pickPositionPass.render(obj);
        this.needsPickingDraw = true;
        return this.pickPositionPass.readPositionFromPickingBuffer(x, y);
    },

    getRenderObjectByRay: function (xml3dRay, viewMat, projMat) {
        var intersectedObjects = this.scene.findRayIntersections(xml3dRay);
        this.pickObjectPass.render(intersectedObjects, viewMat, projMat);
        //Target the middle of the buffer
        var x = Math.floor(this.pickObjectPass.output.getWidth() / 2 / this.pickObjectPass.output.getScale());
        var y = Math.floor(this.pickObjectPass.output.getHeight() / 2 / this.pickObjectPass.output.getScale());
        return this.pickObjectPass.getRenderObjectFromPickingBuffer(x, y, intersectedObjects);

    },

    getWorldSpaceNormalByRay: function (ray, intersectedObject, viewMat, projMat) {
        if (!intersectedObject)
            return null;
        this.pickNormalPass.render(intersectedObject, viewMat, projMat);
        var x = Math.floor(this.pickNormalPass.output.getWidth() / 2 / this.pickNormalPass.output.getScale());
        var y = Math.floor(this.pickNormalPass.output.getHeight() / 2 / this.pickNormalPass.output.getScale());
        return this.pickNormalPass.readNormalFromPickingBuffer(x, y);

    }, getWorldSpacePositionByRay: function (ray, intersectedObject, viewMat, projMat) {
        if (!intersectedObject)
            return null;
        this.pickPositionPass.render(intersectedObject, viewMat, projMat);
        var x = Math.floor(this.pickPositionPass.output.getWidth() / 2 / this.pickPositionPass.output.getScale());
        var y = Math.floor(this.pickPositionPass.output.getHeight() / 2 / this.pickPositionPass.output.getScale());
        return this.pickPositionPass.readPositionFromPickingBuffer(x, y);

    },

    calculateMatricesForRay: function (ray, viewMat, projMat) {
        this.rayCamera.updatePosition(ray.origin._data);
        this.rayCamera.updateOrientation(this.calculateOrientationForRayDirection(ray));
        this.rayCamera.getWorldToViewMatrix(viewMat);
        var aspect = this.pickObjectPass.output.getWidth() / this.pickObjectPass.output.getHeight();
        this.rayCamera.getProjectionMatrix(projMat, aspect);
    },

    calculateOrientationForRayDirection: (function () {
        var tmpX = XML3D.math.vec3.create();
        var tmpY = XML3D.math.vec3.create();
        var tmpZ = XML3D.math.vec3.create();
        var up = XML3D.math.vec3.create();
        var q = XML3D.math.quat.create();
        var m = XML3D.math.mat4.create();

        return function (ray) {
            XML3D.math.vec3.set(up, 0, 1, 0);
            XML3D.math.vec3.cross(tmpX, ray.direction._data, up);
            if (!XML3D.math.vec3.length(tmpX)) {
                XML3D.math.vec3.set(tmpX, 1, 0, 0);
            }
            XML3D.math.vec3.cross(tmpY, tmpX, ray.direction._data);
            XML3D.math.vec3.negate(tmpZ, ray.direction._data);

            XML3D.math.quat.setFromBasis(tmpX, tmpY, tmpZ, q);
            XML3D.math.mat4.fromRotationTranslation(m, q, [0, 0, 0]);
            return m;
        }
    })(),

    needsRedraw: function () {
        return this.needsDraw;
    },

    renderToCanvas: function () {
        this.prepareRendering();
        this.renderInterface.getRenderPipeline().render(this.scene);
        var stats = this.renderInterface.getRenderPipeline().getRenderStats();
        XML3D.debug.logDebug("Rendered to Canvas");
        this.needsDraw = false;
        return stats;
    },

    getRenderObjectFromPickingBuffer: function (x, y) {
        y = canvasToGlY(this._canvasHandler.getCanvas(), y);
        if (this.needsPickingDraw) {
            this.prepareRendering();
            this.scene.updateReadyObjectsFromActiveView(this.pickObjectPass.output.getWidth() / this.pickObjectPass.output.getHeight());
            this.pickObjectPass.render(this.scene.ready);
            this.needsPickingDraw = false;
            XML3D.debug.logDebug("Rendered Picking Buffer");
        }
        this.pickedObject = this.pickObjectPass.getRenderObjectFromPickingBuffer(x, y, this.scene.ready);
        return this.pickedObject;
    },

    prepareRendering: function () {
        this.scene.update();
    },

    /**
     * Uses gluUnProject() to transform the 2D screen point to a 3D ray.
     * Not tested!!
     *
     * @param {number} canvasX
     * @param {number} canvasY
     */
    generateRay: (function () {

        var c_viewMatrix = XML3D.math.mat4.create();
        var c_projectionMatrix = XML3D.math.mat4.create();

        return function (canvasX, canvasY) {

            var glY = canvasToGlY(this._canvasHandler.getCanvas(), canvasY);

            // setup input to unproject
            var viewport = new Array();
            viewport[0] = 0;
            viewport[1] = 0;
            viewport[2] = this.width;
            viewport[3] = this.height;

            // get view and projection matrix arrays
            var view = this.scene.getActiveView();
            view.getWorldToViewMatrix(c_viewMatrix);
            view.getProjectionMatrix(c_projectionMatrix, viewport[2] / viewport[3]);

            var ray = new window.XML3DRay();

            var nearHit = new Array();
            var farHit = new Array();

            // do unprojections
            if (false === GLU.unProject(canvasX, glY, 0, c_viewMatrix, c_projectionMatrix, viewport, nearHit)) {
                return ray;
            }

            if (false === GLU.unProject(canvasX, glY, 1, c_viewMatrix, c_projectionMatrix, viewport, farHit)) {
                return ray;
            }

            // calculate ray
            XML3D.math.mat4.invert(c_viewMatrix, c_viewMatrix);
            var viewPos = new window.XML3DVec3(c_viewMatrix[12], c_viewMatrix[13], c_viewMatrix[14]);

            ray.origin.set(viewPos);
            ray.direction.set(farHit[0] - nearHit[0], farHit[1] - nearHit[1], farHit[2] - nearHit[2]);
            ray.direction.set(ray.direction.normalize());

            return ray;
        }
    }()),

    dispose: function () {
        this.scene.clear();
    },

    getRenderInterface: function () {
        return this.renderInterface;
    },

    onFlagsChange: function (key) {
        if (key == OPTION_SSAO) {
            this.scene.shaderFactory.setShaderRecompile();
            this.createDefaultPipelines();
        }
    }
});

module.exports = GLRenderer;
<commit_msg>Flip the needsDraw flag before rendering the frame to avoid rendering broken frames endlessly<commit_after>var GLContext = require("./base/context");
var GLScene = require("./scene/glscene");
var GLScaledRenderTarget = require("./base/rendertarget.js").GLScaledRenderTarget;
var DataChangeListener = require("../renderer/tools/datachangelistener.js");
var RenderInterface = require("./render-interface");
var PickObjectRenderPass= require("./render-passes/pick-object.js");
var PickPositionRenderPass = require("./render-passes/pick-position.js");
var PickNormalRenderPass = require("./render-passes/pick-normal.js");
var ForwardRenderTree = require("./render-trees/forward.js");
var GLU = require("../../contrib/glu.js");
var MAX_PICK_BUFFER_DIMENSION = 512;

var OPTION_SSAO = "renderer-ssao";
var FLAGS = {};
FLAGS[OPTION_SSAO] = {defaultValue: false, recompileOnChange: true};
for (var flag in FLAGS) {
    XML3D.options.register(flag, FLAGS[flag].defaultValue);
}

/**
 * Convert the given y-coordinate on the canvas to a y-coordinate appropriate in
 * the GL context. The y-coordinate gets turned upside-down. The lowest possible
 * canvas coordinate is 0, so we need to subtract 1 from the height, too.
 *
 * @param {HTMLCanvasElement} canvas
 * @param {number} y
 * @return {number} the converted y-coordinate
 */
var canvasToGlY = function (canvas, y) {
    return canvas.height - y - 1;
};
/**
 * @interface
 */
var IRenderer = function () {
};

IRenderer.prototype.renderToCanvas = function () {
};
IRenderer.prototype.handleResizeEvent = function (width, height) {
};
IRenderer.prototype.requestRedraw = function (reason) {
};
IRenderer.prototype.needsRedraw = function () {
};
IRenderer.prototype.getWorldSpaceNormalByPoint = function (obj, x, y) {
};
IRenderer.prototype.getWorldSpacePositionByPoint = function (obj, x, y) {
};
IRenderer.prototype.getRenderObjectFromPickingBuffer = function (x, y) {
};
IRenderer.prototype.generateRay = function (x, y) {
};
IRenderer.prototype.dispose = function () {
};

/**
 * @param {Element} element The <xml3d> Element
 * @implements {IRenderer}
 * @constructor
 */
var GLRenderer = function (element, canvasHandler) {

    this._canvasHandler = canvasHandler;
    var canvas = this._canvasHandler.getCanvas();
    this.width = canvas.clientWidth;
    this.height = canvas.clientHeight;

    this.context = new GLContext(canvas, this._canvasHandler.id);
    this.scene = new GLScene(this.context);

    var factory = XML3D.base.xml3dFormatHandler.getFactory(XML3D.webgl, this._canvasHandler.id);
    factory.setScene(this.scene);
    factory.setRenderer(this);

    var xml3dAdapter = factory.getAdapter(element);
    xml3dAdapter.traverse(function () {
    });

    this.scene.rootNode.setVisible(true);

    /** @type {RenderObject} */
    this.pickedObject = null;

    this.needsDraw = true;
    this.needsPickingDraw = true;
    this.context.requestRedraw = this.requestRedraw.bind(this);

    //Currently used as a helper to calculate view and projection matrices for ray casting, since the scene
    //must be rendered from the point of view of the ray
    this.rayCamera = this.scene.createRenderView();

    this.initGL();
    this.changeListener = new DataChangeListener(this);

    this.renderInterface = this.createRenderInterface();
    this.createDefaultPipelines();
    XML3D.options.addObserver(this.onFlagsChange.bind(this));
};

// Just to satisfy jslint
GLRenderer.prototype.generateRay = function () {
};

XML3D.extend(GLRenderer.prototype, {
    initGL: function () {
        var gl = this.context.gl;

        gl.clearColor(0, 0, 0, 0);
        gl.clearDepth(1);
        gl.clearStencil(0);

        gl.enable(gl.DEPTH_TEST);
        gl.depthFunc(gl.LEQUAL);

        gl.frontFace(gl.CCW);
        gl.cullFace(gl.BACK);
        gl.disable(gl.CULL_FACE);

        gl.blendEquation(gl.FUNC_ADD);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        gl.disable(gl.BLEND);

        gl.viewport(0, 0, this.width, this.height);

        gl.pixelStorei(gl.PACK_ALIGNMENT, 1);
        gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
        gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL);

    },

    handleResizeEvent: function (width, height) {
        this.width = width;
        this.height = height;
        this.context.handleResizeEvent(width, height);
        this.createDefaultPipelines();
        this.scene.handleResizeEvent(width, height);
        this.needsDraw = this.needsPickingDraw = true;
    },

    createDefaultPipelines: function () {
        var pipeline = new ForwardRenderTree(this.renderInterface, XML3D.options.getValue(OPTION_SSAO));
        this.renderInterface.setRenderPipeline(pipeline);

        var pickTarget = new GLScaledRenderTarget(this.context, MAX_PICK_BUFFER_DIMENSION, {
            width: this.context.canvasTarget.width,
            height: this.context.canvasTarget.height,
            colorFormat: this.context.gl.RGBA,
            depthFormat: this.context.gl.DEPTH_COMPONENT16,
            stencilFormat: null,
            depthAsRenderbuffer: true
        });
        this.pickObjectPass = new PickObjectRenderPass(this.renderInterface, pickTarget);
        this.pickPositionPass = new PickPositionRenderPass(this.renderInterface, pickTarget);
        this.pickNormalPass = new PickNormalRenderPass(this.renderInterface, pickTarget);
    },

    createRenderInterface: function () {
        return new RenderInterface(this.context, this.scene);
        //TODO need to provide an interface for creating shaders, buffers and so on
    },

    requestRedraw: function (reason) {
        XML3D.debug.logDebug("Request redraw because:", reason);
        this.needsDraw = true;
        this.needsPickingDraw = true;
    },

    getWorldSpaceNormalByPoint: function (x, y, object) {
        var obj = object || this.pickedObject;
        if (!obj)
            return null;
        y = canvasToGlY(this._canvasHandler.getCanvas(), y);
        this.pickNormalPass.render(obj);
        this.needsPickingDraw = true;
        return this.pickNormalPass.readNormalFromPickingBuffer(x, y);
    },

    getWorldSpacePositionByPoint: function (x, y, object) {
        var obj = object || this.pickedObject;
        if (!obj)
            return null;
        y = canvasToGlY(this._canvasHandler.getCanvas(), y);
        this.pickPositionPass.render(obj);
        this.needsPickingDraw = true;
        return this.pickPositionPass.readPositionFromPickingBuffer(x, y);
    },

    getRenderObjectByRay: function (xml3dRay, viewMat, projMat) {
        var intersectedObjects = this.scene.findRayIntersections(xml3dRay);
        this.pickObjectPass.render(intersectedObjects, viewMat, projMat);
        //Target the middle of the buffer
        var x = Math.floor(this.pickObjectPass.output.getWidth() / 2 / this.pickObjectPass.output.getScale());
        var y = Math.floor(this.pickObjectPass.output.getHeight() / 2 / this.pickObjectPass.output.getScale());
        return this.pickObjectPass.getRenderObjectFromPickingBuffer(x, y, intersectedObjects);

    },

    getWorldSpaceNormalByRay: function (ray, intersectedObject, viewMat, projMat) {
        if (!intersectedObject)
            return null;
        this.pickNormalPass.render(intersectedObject, viewMat, projMat);
        var x = Math.floor(this.pickNormalPass.output.getWidth() / 2 / this.pickNormalPass.output.getScale());
        var y = Math.floor(this.pickNormalPass.output.getHeight() / 2 / this.pickNormalPass.output.getScale());
        return this.pickNormalPass.readNormalFromPickingBuffer(x, y);

    }, getWorldSpacePositionByRay: function (ray, intersectedObject, viewMat, projMat) {
        if (!intersectedObject)
            return null;
        this.pickPositionPass.render(intersectedObject, viewMat, projMat);
        var x = Math.floor(this.pickPositionPass.output.getWidth() / 2 / this.pickPositionPass.output.getScale());
        var y = Math.floor(this.pickPositionPass.output.getHeight() / 2 / this.pickPositionPass.output.getScale());
        return this.pickPositionPass.readPositionFromPickingBuffer(x, y);

    },

    calculateMatricesForRay: function (ray, viewMat, projMat) {
        this.rayCamera.updatePosition(ray.origin._data);
        this.rayCamera.updateOrientation(this.calculateOrientationForRayDirection(ray));
        this.rayCamera.getWorldToViewMatrix(viewMat);
        var aspect = this.pickObjectPass.output.getWidth() / this.pickObjectPass.output.getHeight();
        this.rayCamera.getProjectionMatrix(projMat, aspect);
    },

    calculateOrientationForRayDirection: (function () {
        var tmpX = XML3D.math.vec3.create();
        var tmpY = XML3D.math.vec3.create();
        var tmpZ = XML3D.math.vec3.create();
        var up = XML3D.math.vec3.create();
        var q = XML3D.math.quat.create();
        var m = XML3D.math.mat4.create();

        return function (ray) {
            XML3D.math.vec3.set(up, 0, 1, 0);
            XML3D.math.vec3.cross(tmpX, ray.direction._data, up);
            if (!XML3D.math.vec3.length(tmpX)) {
                XML3D.math.vec3.set(tmpX, 1, 0, 0);
            }
            XML3D.math.vec3.cross(tmpY, tmpX, ray.direction._data);
            XML3D.math.vec3.negate(tmpZ, ray.direction._data);

            XML3D.math.quat.setFromBasis(tmpX, tmpY, tmpZ, q);
            XML3D.math.mat4.fromRotationTranslation(m, q, [0, 0, 0]);
            return m;
        }
    })(),

    needsRedraw: function () {
        return this.needsDraw;
    },

    renderToCanvas: function () {
        this.needsDraw = false; //Set this early to avoid endless rendering if an exception is thrown during rendering
        this.prepareRendering();
        this.renderInterface.getRenderPipeline().render(this.scene);
        var stats = this.renderInterface.getRenderPipeline().getRenderStats();
        XML3D.debug.logDebug("Rendered to Canvas");
        return stats;
    },

    getRenderObjectFromPickingBuffer: function (x, y) {
        y = canvasToGlY(this._canvasHandler.getCanvas(), y);
        if (this.needsPickingDraw) {
            this.needsPickingDraw = false;
            this.prepareRendering();
            this.scene.updateReadyObjectsFromActiveView(this.pickObjectPass.output.getWidth() / this.pickObjectPass.output.getHeight());
            this.pickObjectPass.render(this.scene.ready);
            XML3D.debug.logDebug("Rendered Picking Buffer");
        }
        this.pickedObject = this.pickObjectPass.getRenderObjectFromPickingBuffer(x, y, this.scene.ready);
        return this.pickedObject;
    },

    prepareRendering: function () {
        this.scene.update();
    },

    /**
     * Uses gluUnProject() to transform the 2D screen point to a 3D ray.
     * Not tested!!
     *
     * @param {number} canvasX
     * @param {number} canvasY
     */
    generateRay: (function () {

        var c_viewMatrix = XML3D.math.mat4.create();
        var c_projectionMatrix = XML3D.math.mat4.create();

        return function (canvasX, canvasY) {

            var glY = canvasToGlY(this._canvasHandler.getCanvas(), canvasY);

            // setup input to unproject
            var viewport = new Array();
            viewport[0] = 0;
            viewport[1] = 0;
            viewport[2] = this.width;
            viewport[3] = this.height;

            // get view and projection matrix arrays
            var view = this.scene.getActiveView();
            view.getWorldToViewMatrix(c_viewMatrix);
            view.getProjectionMatrix(c_projectionMatrix, viewport[2] / viewport[3]);

            var ray = new window.XML3DRay();

            var nearHit = new Array();
            var farHit = new Array();

            // do unprojections
            if (false === GLU.unProject(canvasX, glY, 0, c_viewMatrix, c_projectionMatrix, viewport, nearHit)) {
                return ray;
            }

            if (false === GLU.unProject(canvasX, glY, 1, c_viewMatrix, c_projectionMatrix, viewport, farHit)) {
                return ray;
            }

            // calculate ray
            XML3D.math.mat4.invert(c_viewMatrix, c_viewMatrix);
            var viewPos = new window.XML3DVec3(c_viewMatrix[12], c_viewMatrix[13], c_viewMatrix[14]);

            ray.origin.set(viewPos);
            ray.direction.set(farHit[0] - nearHit[0], farHit[1] - nearHit[1], farHit[2] - nearHit[2]);
            ray.direction.set(ray.direction.normalize());

            return ray;
        }
    }()),

    dispose: function () {
        this.scene.clear();
    },

    getRenderInterface: function () {
        return this.renderInterface;
    },

    onFlagsChange: function (key) {
        if (key == OPTION_SSAO) {
            this.scene.shaderFactory.setShaderRecompile();
            this.createDefaultPipelines();
        }
    }
});

module.exports = GLRenderer;
<|endoftext|>