<commit_before>goog.provide('ol.renderer.canvas.VectorTileLayer');

goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.vec.Mat4');
goog.require('ol.Feature');
goog.require('ol.TileRange');
goog.require('ol.TileState');
goog.require('ol.VectorTile');
goog.require('ol.ViewHint');
goog.require('ol.dom');
goog.require('ol.extent');
goog.require('ol.layer.VectorTile');
goog.require('ol.proj.Units');
goog.require('ol.render.EventType');
goog.require('ol.render.canvas.ReplayGroup');
goog.require('ol.renderer.canvas.Layer');
goog.require('ol.renderer.vector');
goog.require('ol.size');
goog.require('ol.source.VectorTile');
goog.require('ol.tilecoord');
goog.require('ol.vec.Mat4');

/**
 * @constructor
 * @extends {ol.renderer.canvas.Layer}
 * @param {ol.layer.VectorTile} layer VectorTile layer.
 */
ol.renderer.canvas.VectorTileLayer = function(layer) {

  goog.base(this, layer);

  /**
   * @private
   * @type {CanvasRenderingContext2D}
   */
  this.context_ = ol.dom.createCanvasContext2D();

  /**
   * @private
   * @type {boolean}
   */
  this.dirty_ = false;

  /**
   * @private
   * @type {Array.<ol.VectorTile>}
   */
  this.renderedTiles_ = [];

  /**
   * @private
   * @type {ol.Extent}
   */
  this.tmpExtent_ = ol.extent.createEmpty();

  /**
   * @private
   * @type {ol.Size}
   */
  this.tmpSize_ = [NaN, NaN];

  /**
   * @private
   * @type {!goog.vec.Mat4.Number}
   */
  this.tmpTransform_ = goog.vec.Mat4.createNumber();

};
goog.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.Layer);

/**
 * @inheritDoc
 */
ol.renderer.canvas.VectorTileLayer.prototype.composeFrame =
    function(frameState, layerState, context) {

  var pixelRatio = frameState.pixelRatio;
  var skippedFeatureUids = layerState.managed ?
      frameState.skippedFeatureUids : {};
  var viewState = frameState.viewState;
  var center = viewState.center;
  var projection = viewState.projection;
  var resolution = viewState.resolution;
  var rotation = viewState.rotation;
  var layer = this.getLayer();
  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');

  var transform = this.getTransform(frameState, 0);

  this.dispatchPreComposeEvent(context, frameState, transform);

  var replayContext;
  if (layer.hasListener(ol.render.EventType.RENDER)) {
    // resize and clear
    this.context_.canvas.width = context.canvas.width;
    this.context_.canvas.height = context.canvas.height;
    replayContext = this.context_;
  } else {
    replayContext = context;
  }
  // for performance reasons, context.save / context.restore is not used
  // to save and restore the transformation matrix and the opacity.
  // see http://jsperf.com/context-save-restore-versus-variable
  var alpha = replayContext.globalAlpha;
  replayContext.globalAlpha = layerState.opacity;

  var tilesToDraw = this.renderedTiles_;
  var tileGrid = source.getTileGrid();

  var currentZ, i, ii, origin, scale, tile, tileExtent, tileSize;
  var tilePixelRatio, tilePixelResolution, tilePixelSize, tileResolution;
  for (i = 0, ii = tilesToDraw.length; i < ii; ++i) {
    tile = tilesToDraw[i];
    currentZ = tile.getTileCoord()[0];
    tileSize = tileGrid.getTileSize(currentZ);
    tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection);
    tilePixelRatio = tilePixelSize[0] /
        ol.size.toSize(tileSize, this.tmpSize_)[0];
    tileResolution = tileGrid.getResolution(currentZ);
    tilePixelResolution = tileResolution / tilePixelRatio;
    if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) {
      origin = ol.extent.getTopLeft(tileGrid.getTileCoordExtent(
          tile.getTileCoord(), this.tmpExtent_));
      transform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
          pixelRatio * frameState.size[0] / 2,
          pixelRatio * frameState.size[1] / 2,
          pixelRatio * tilePixelResolution / resolution,
          pixelRatio * tilePixelResolution / resolution,
          viewState.rotation,
          (origin[0] - center[0]) / tilePixelResolution,
          (center[1] - origin[1]) / tilePixelResolution);
    }
    tile.getReplayState().replayGroup.replay(replayContext, pixelRatio,
        transform, rotation, skippedFeatureUids);
  }

  transform = this.getTransform(frameState, 0);

  if (replayContext != context) {
    this.dispatchRenderEvent(replayContext, frameState, transform);
    context.drawImage(replayContext.canvas, 0, 0);
  }
  replayContext.globalAlpha = alpha;

  this.dispatchPostComposeEvent(context, frameState, transform);
};

/**
 * @param {ol.VectorTile} tile Tile.
 * @param {ol.layer.VectorTile} layer Vector tile layer.
 * @param {number} pixelRatio Pixel ratio.
 */
ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile,
    layer, pixelRatio) {
  var revision = layer.getRevision();
  var renderOrder = layer.getRenderOrder() || null;

  var replayState = tile.getReplayState();
  if (!replayState.dirty && replayState.renderedRevision == revision &&
      replayState.renderedRenderOrder == renderOrder) {
    return;
  }

  // FIXME dispose of old replayGroup in post render
  goog.dispose(replayState.replayGroup);
  replayState.replayGroup = null;
  replayState.dirty = false;

  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');
  var tileGrid = source.getTileGrid();
  var tileCoord = tile.getTileCoord();
  var pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS;
  var extent;
  if (pixelSpace) {
    var tilePixelSize = source.getTilePixelSize(tileCoord[0], pixelRatio,
        tile.getProjection());
    extent = [0, 0, tilePixelSize[0], tilePixelSize[1]];
  } else {
    extent = tileGrid.getTileCoordExtent(tileCoord);
  }
  var resolution = tileGrid.getResolution(tileCoord[0]);
  var tileResolution = pixelSpace ? source.getTilePixelRatio() : resolution;
  replayState.dirty = false;
  var replayGroup = new ol.render.canvas.ReplayGroup(0, extent,
      tileResolution, layer.getRenderBuffer());
  var squaredTolerance = ol.renderer.vector.getSquaredTolerance(
      tileResolution, pixelRatio);

  /**
   * @param {ol.Feature|ol.render.Feature} feature Feature.
   * @this {ol.renderer.canvas.VectorTileLayer}
   */
  function renderFeature(feature) {
    var styles;
    var styleFunction = feature.getStyleFunction();
    if (styleFunction) {
      goog.asserts.assertInstanceof(feature, ol.Feature, 'Got an ol.Feature');
      styles = styleFunction.call(feature, resolution);
    } else {
      styleFunction = layer.getStyleFunction();
      if (styleFunction) {
        styles = styleFunction(feature, resolution);
      }
    }
    if (styles) {
      if (!goog.isArray(styles)) {
        styles = [styles];
      }
      var dirty = this.renderFeature(feature, squaredTolerance, styles,
          replayGroup);
      this.dirty_ = this.dirty_ || dirty;
      replayState.dirty = replayState.dirty || dirty;
    }
  }

  var features = tile.getFeatures();
  if (renderOrder && renderOrder !== replayState.renderedRenderOrder) {
    features.sort(renderOrder);
  }
  features.forEach(renderFeature, this);

  replayGroup.finish();

  replayState.renderedRevision = revision;
  replayState.renderedRenderOrder = renderOrder;
  replayState.replayGroup = replayGroup;
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate =
    function(coordinate, frameState, callback, thisArg) {
  var resolution = frameState.viewState.resolution;
  var rotation = frameState.viewState.rotation;
  var layer = this.getLayer();
  var layerState = frameState.layerStates[goog.getUid(layer)];
  /** @type {Object.<string, boolean>} */
  var features = {};

  var replayables = this.renderedTiles_;
  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');
  var tileGrid = source.getTileGrid();
  var found, tileSpaceCoordinate;
  var i, ii, origin, replayGroup;
  var tile, tileCoord, tileExtent, tilePixelRatio, tileResolution, tileSize;
  for (i = 0, ii = replayables.length; i < ii; ++i) {
    tile = replayables[i];
    tileCoord = tile.getTileCoord();
    tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord,
        this.tmpExtent_);
    if (!ol.extent.containsCoordinate(tileExtent, coordinate)) {
      continue;
    }
    if (tile.getProjection().getUnits() === ol.proj.Units.TILE_PIXELS) {
      origin = ol.extent.getTopLeft(tileExtent);
      tilePixelRatio = source.getTilePixelRatio();
      tileResolution = tileGrid.getResolution(tileCoord[0]) / tilePixelRatio;
      tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0]));
      tileSpaceCoordinate = [
        (coordinate[0] - origin[0]) / tileResolution,
        (origin[1] - coordinate[1]) / tileResolution
      ];
      resolution = tilePixelRatio;
    } else {
      tileSpaceCoordinate = coordinate;
    }
    replayGroup = tile.getReplayState().replayGroup;
    found = found || replayGroup.forEachFeatureAtCoordinate(
        tileSpaceCoordinate, resolution, rotation,
        layerState.managed ? frameState.skippedFeatureUids : {},
        /**
         * @param {ol.Feature|ol.render.Feature} feature Feature.
         * @return {?} Callback result.
         */
        function(feature) {
          goog.asserts.assert(feature, 'received a feature');
          var key = goog.getUid(feature).toString();
          if (!(key in features)) {
            features[key] = true;
            return callback.call(thisArg, feature, layer);
          }
        });
  }
  return found;
};

/**
 * Handle changes in image style state.
 * @param {goog.events.Event} event Image style change event.
 * @private
 */
ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ =
    function(event) {
  this.renderIfReadyAndVisible();
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame =
    function(frameState, layerState) {
  var layer = /** @type {ol.layer.Vector} */ (this.getLayer());
  goog.asserts.assertInstanceof(layer, ol.layer.VectorTile,
      'layer is an instance of ol.layer.VectorTile');
  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');

  this.updateAttributions(
      frameState.attributions, source.getAttributions());
  this.updateLogos(frameState, source);

  var animating = frameState.viewHints[ol.ViewHint.ANIMATING];
  var interacting = frameState.viewHints[ol.ViewHint.INTERACTING];
  var updateWhileAnimating = layer.getUpdateWhileAnimating();
  var updateWhileInteracting = layer.getUpdateWhileInteracting();

  if (!this.dirty_ && (!updateWhileAnimating && animating) ||
      (!updateWhileInteracting && interacting)) {
    return true;
  }

  var extent = frameState.extent;
  if (layerState.extent) {
    extent = ol.extent.getIntersection(extent, layerState.extent);
  }
  if (ol.extent.isEmpty(extent)) {
    // Return false to prevent the rendering of the layer.
    return false;
  }

  var viewState = frameState.viewState;
  var projection = viewState.projection;
  var resolution = viewState.resolution;
  var pixelRatio = frameState.pixelRatio;

  var tileGrid = source.getTileGrid();
  var resolutions = tileGrid.getResolutions();
  var z = resolutions.length - 1;
  while (z > 0 && resolutions[z] < resolution) {
    --z;
  }
  var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
  this.updateUsedTiles(frameState.usedTiles, source, z, tileRange);
  this.manageTilePyramid(frameState, source, tileGrid, pixelRatio,
      projection, extent, z, layer.getPreload());
  this.scheduleExpireCache(frameState, source);

  /**
   * @type {Object.<number, Object.<string, ol.VectorTile>>}
   */
  var tilesToDrawByZ = {};
  tilesToDrawByZ[z] = {};

  var findLoadedTiles = this.createLoadedTileFinder(source, projection,
      tilesToDrawByZ);

  var useInterimTilesOnError = layer.getUseInterimTilesOnError();

  var tmpExtent = this.tmpExtent_;
  var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
  var childTileRange, fullyLoaded, tile, tileState, x, y;
  for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {

      tile = source.getTile(z, x, y, pixelRatio, projection);
      goog.asserts.assertInstanceof(tile, ol.VectorTile,
          'Tile is an ol.VectorTile');
      tileState = tile.getState();
      if (tileState == ol.TileState.LOADED ||
          tileState == ol.TileState.EMPTY ||
          (tileState == ol.TileState.ERROR && !useInterimTilesOnError)) {
        tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
        continue;
      }

      fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
          tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
      if (!fullyLoaded) {
        childTileRange = tileGrid.getTileCoordChildTileRange(
            tile.tileCoord, tmpTileRange, tmpExtent);
        if (childTileRange) {
          findLoadedTiles(z + 1, childTileRange);
        }
      }

    }
  }

  this.dirty_ = false;

  /** @type {Array.<number>} */
  var zs = Object.keys(tilesToDrawByZ).map(Number);
  zs.sort();
  var replayables = [];
  var i, ii, currentZ, tileCoordKey, tilesToDraw;
  for (i = 0, ii = zs.length; i < ii; ++i) {
    currentZ = zs[i];
    tilesToDraw = tilesToDrawByZ[currentZ];
    for (tileCoordKey in tilesToDraw) {
      tile = tilesToDraw[tileCoordKey];
      if (tile.getState() == ol.TileState.LOADED) {
        replayables.push(tile);
        this.createReplayGroup(tile, layer, pixelRatio);
      }
    }
  }

  this.renderedTiles_ = replayables;

  return true;
};

/**
 * @param {ol.Feature|ol.render.Feature} feature Feature.
 * @param {number} squaredTolerance Squared tolerance.
 * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
 *     styles.
 * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
 * @return {boolean} `true` if an image is loading.
 */
ol.renderer.canvas.VectorTileLayer.prototype.renderFeature =
    function(feature, squaredTolerance, styles, replayGroup) {
  if (!styles) {
    return false;
  }
  var loading = false;
  if (goog.isArray(styles)) {
    for (var i = 0, ii = styles.length; i < ii; ++i) {
      loading = ol.renderer.vector.renderFeature(
          replayGroup, feature, styles[i], squaredTolerance,
          this.handleStyleImageChange_, this) || loading;
    }
  } else {
    loading = ol.renderer.vector.renderFeature(
        replayGroup, feature, styles, squaredTolerance,
        this.handleStyleImageChange_, this) || loading;
  }
  return loading;
};
<commit_msg>Remove unused local variables<commit_after>goog.provide('ol.renderer.canvas.VectorTileLayer');

goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.vec.Mat4');
goog.require('ol.Feature');
goog.require('ol.TileRange');
goog.require('ol.TileState');
goog.require('ol.VectorTile');
goog.require('ol.ViewHint');
goog.require('ol.dom');
goog.require('ol.extent');
goog.require('ol.layer.VectorTile');
goog.require('ol.proj.Units');
goog.require('ol.render.EventType');
goog.require('ol.render.canvas.ReplayGroup');
goog.require('ol.renderer.canvas.Layer');
goog.require('ol.renderer.vector');
goog.require('ol.size');
goog.require('ol.source.VectorTile');
goog.require('ol.tilecoord');
goog.require('ol.vec.Mat4');

/**
 * @constructor
 * @extends {ol.renderer.canvas.Layer}
 * @param {ol.layer.VectorTile} layer VectorTile layer.
 */
ol.renderer.canvas.VectorTileLayer = function(layer) {

  goog.base(this, layer);

  /**
   * @private
   * @type {CanvasRenderingContext2D}
   */
  this.context_ = ol.dom.createCanvasContext2D();

  /**
   * @private
   * @type {boolean}
   */
  this.dirty_ = false;

  /**
   * @private
   * @type {Array.<ol.VectorTile>}
   */
  this.renderedTiles_ = [];

  /**
   * @private
   * @type {ol.Extent}
   */
  this.tmpExtent_ = ol.extent.createEmpty();

  /**
   * @private
   * @type {ol.Size}
   */
  this.tmpSize_ = [NaN, NaN];

  /**
   * @private
   * @type {!goog.vec.Mat4.Number}
   */
  this.tmpTransform_ = goog.vec.Mat4.createNumber();

};
goog.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.Layer);

/**
 * @inheritDoc
 */
ol.renderer.canvas.VectorTileLayer.prototype.composeFrame =
    function(frameState, layerState, context) {

  var pixelRatio = frameState.pixelRatio;
  var skippedFeatureUids = layerState.managed ?
      frameState.skippedFeatureUids : {};
  var viewState = frameState.viewState;
  var center = viewState.center;
  var projection = viewState.projection;
  var resolution = viewState.resolution;
  var rotation = viewState.rotation;
  var layer = this.getLayer();
  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');

  var transform = this.getTransform(frameState, 0);

  this.dispatchPreComposeEvent(context, frameState, transform);

  var replayContext;
  if (layer.hasListener(ol.render.EventType.RENDER)) {
    // resize and clear
    this.context_.canvas.width = context.canvas.width;
    this.context_.canvas.height = context.canvas.height;
    replayContext = this.context_;
  } else {
    replayContext = context;
  }
  // for performance reasons, context.save / context.restore is not used
  // to save and restore the transformation matrix and the opacity.
  // see http://jsperf.com/context-save-restore-versus-variable
  var alpha = replayContext.globalAlpha;
  replayContext.globalAlpha = layerState.opacity;

  var tilesToDraw = this.renderedTiles_;
  var tileGrid = source.getTileGrid();

  var currentZ, i, ii, origin, tile, tileSize;
  var tilePixelRatio, tilePixelResolution, tilePixelSize, tileResolution;
  for (i = 0, ii = tilesToDraw.length; i < ii; ++i) {
    tile = tilesToDraw[i];
    currentZ = tile.getTileCoord()[0];
    tileSize = tileGrid.getTileSize(currentZ);
    tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection);
    tilePixelRatio = tilePixelSize[0] /
        ol.size.toSize(tileSize, this.tmpSize_)[0];
    tileResolution = tileGrid.getResolution(currentZ);
    tilePixelResolution = tileResolution / tilePixelRatio;
    if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) {
      origin = ol.extent.getTopLeft(tileGrid.getTileCoordExtent(
          tile.getTileCoord(), this.tmpExtent_));
      transform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
          pixelRatio * frameState.size[0] / 2,
          pixelRatio * frameState.size[1] / 2,
          pixelRatio * tilePixelResolution / resolution,
          pixelRatio * tilePixelResolution / resolution,
          viewState.rotation,
          (origin[0] - center[0]) / tilePixelResolution,
          (center[1] - origin[1]) / tilePixelResolution);
    }
    tile.getReplayState().replayGroup.replay(replayContext, pixelRatio,
        transform, rotation, skippedFeatureUids);
  }

  transform = this.getTransform(frameState, 0);

  if (replayContext != context) {
    this.dispatchRenderEvent(replayContext, frameState, transform);
    context.drawImage(replayContext.canvas, 0, 0);
  }
  replayContext.globalAlpha = alpha;

  this.dispatchPostComposeEvent(context, frameState, transform);
};

/**
 * @param {ol.VectorTile} tile Tile.
 * @param {ol.layer.VectorTile} layer Vector tile layer.
 * @param {number} pixelRatio Pixel ratio.
 */
ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile,
    layer, pixelRatio) {
  var revision = layer.getRevision();
  var renderOrder = layer.getRenderOrder() || null;

  var replayState = tile.getReplayState();
  if (!replayState.dirty && replayState.renderedRevision == revision &&
      replayState.renderedRenderOrder == renderOrder) {
    return;
  }

  // FIXME dispose of old replayGroup in post render
  goog.dispose(replayState.replayGroup);
  replayState.replayGroup = null;
  replayState.dirty = false;

  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');
  var tileGrid = source.getTileGrid();
  var tileCoord = tile.getTileCoord();
  var pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS;
  var extent;
  if (pixelSpace) {
    var tilePixelSize = source.getTilePixelSize(tileCoord[0], pixelRatio,
        tile.getProjection());
    extent = [0, 0, tilePixelSize[0], tilePixelSize[1]];
  } else {
    extent = tileGrid.getTileCoordExtent(tileCoord);
  }
  var resolution = tileGrid.getResolution(tileCoord[0]);
  var tileResolution = pixelSpace ? source.getTilePixelRatio() : resolution;
  replayState.dirty = false;
  var replayGroup = new ol.render.canvas.ReplayGroup(0, extent,
      tileResolution, layer.getRenderBuffer());
  var squaredTolerance = ol.renderer.vector.getSquaredTolerance(
      tileResolution, pixelRatio);

  /**
   * @param {ol.Feature|ol.render.Feature} feature Feature.
   * @this {ol.renderer.canvas.VectorTileLayer}
   */
  function renderFeature(feature) {
    var styles;
    var styleFunction = feature.getStyleFunction();
    if (styleFunction) {
      goog.asserts.assertInstanceof(feature, ol.Feature, 'Got an ol.Feature');
      styles = styleFunction.call(feature, resolution);
    } else {
      styleFunction = layer.getStyleFunction();
      if (styleFunction) {
        styles = styleFunction(feature, resolution);
      }
    }
    if (styles) {
      if (!goog.isArray(styles)) {
        styles = [styles];
      }
      var dirty = this.renderFeature(feature, squaredTolerance, styles,
          replayGroup);
      this.dirty_ = this.dirty_ || dirty;
      replayState.dirty = replayState.dirty || dirty;
    }
  }

  var features = tile.getFeatures();
  if (renderOrder && renderOrder !== replayState.renderedRenderOrder) {
    features.sort(renderOrder);
  }
  features.forEach(renderFeature, this);

  replayGroup.finish();

  replayState.renderedRevision = revision;
  replayState.renderedRenderOrder = renderOrder;
  replayState.replayGroup = replayGroup;
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate =
    function(coordinate, frameState, callback, thisArg) {
  var resolution = frameState.viewState.resolution;
  var rotation = frameState.viewState.rotation;
  var layer = this.getLayer();
  var layerState = frameState.layerStates[goog.getUid(layer)];
  /** @type {Object.<string, boolean>} */
  var features = {};

  var replayables = this.renderedTiles_;
  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');
  var tileGrid = source.getTileGrid();
  var found, tileSpaceCoordinate;
  var i, ii, origin, replayGroup;
  var tile, tileCoord, tileExtent, tilePixelRatio, tileResolution, tileSize;
  for (i = 0, ii = replayables.length; i < ii; ++i) {
    tile = replayables[i];
    tileCoord = tile.getTileCoord();
    tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord,
        this.tmpExtent_);
    if (!ol.extent.containsCoordinate(tileExtent, coordinate)) {
      continue;
    }
    if (tile.getProjection().getUnits() === ol.proj.Units.TILE_PIXELS) {
      origin = ol.extent.getTopLeft(tileExtent);
      tilePixelRatio = source.getTilePixelRatio();
      tileResolution = tileGrid.getResolution(tileCoord[0]) / tilePixelRatio;
      tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0]));
      tileSpaceCoordinate = [
        (coordinate[0] - origin[0]) / tileResolution,
        (origin[1] - coordinate[1]) / tileResolution
      ];
      resolution = tilePixelRatio;
    } else {
      tileSpaceCoordinate = coordinate;
    }
    replayGroup = tile.getReplayState().replayGroup;
    found = found || replayGroup.forEachFeatureAtCoordinate(
        tileSpaceCoordinate, resolution, rotation,
        layerState.managed ? frameState.skippedFeatureUids : {},
        /**
         * @param {ol.Feature|ol.render.Feature} feature Feature.
         * @return {?} Callback result.
         */
        function(feature) {
          goog.asserts.assert(feature, 'received a feature');
          var key = goog.getUid(feature).toString();
          if (!(key in features)) {
            features[key] = true;
            return callback.call(thisArg, feature, layer);
          }
        });
  }
  return found;
};

/**
 * Handle changes in image style state.
 * @param {goog.events.Event} event Image style change event.
 * @private
 */
ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ =
    function(event) {
  this.renderIfReadyAndVisible();
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame =
    function(frameState, layerState) {
  var layer = /** @type {ol.layer.Vector} */ (this.getLayer());
  goog.asserts.assertInstanceof(layer, ol.layer.VectorTile,
      'layer is an instance of ol.layer.VectorTile');
  var source = layer.getSource();
  goog.asserts.assertInstanceof(source, ol.source.VectorTile,
      'Source is an ol.source.VectorTile');

  this.updateAttributions(
      frameState.attributions, source.getAttributions());
  this.updateLogos(frameState, source);

  var animating = frameState.viewHints[ol.ViewHint.ANIMATING];
  var interacting = frameState.viewHints[ol.ViewHint.INTERACTING];
  var updateWhileAnimating = layer.getUpdateWhileAnimating();
  var updateWhileInteracting = layer.getUpdateWhileInteracting();

  if (!this.dirty_ && (!updateWhileAnimating && animating) ||
      (!updateWhileInteracting && interacting)) {
    return true;
  }

  var extent = frameState.extent;
  if (layerState.extent) {
    extent = ol.extent.getIntersection(extent, layerState.extent);
  }
  if (ol.extent.isEmpty(extent)) {
    // Return false to prevent the rendering of the layer.
    return false;
  }

  var viewState = frameState.viewState;
  var projection = viewState.projection;
  var resolution = viewState.resolution;
  var pixelRatio = frameState.pixelRatio;

  var tileGrid = source.getTileGrid();
  var resolutions = tileGrid.getResolutions();
  var z = resolutions.length - 1;
  while (z > 0 && resolutions[z] < resolution) {
    --z;
  }
  var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
  this.updateUsedTiles(frameState.usedTiles, source, z, tileRange);
  this.manageTilePyramid(frameState, source, tileGrid, pixelRatio,
      projection, extent, z, layer.getPreload());
  this.scheduleExpireCache(frameState, source);

  /**
   * @type {Object.<number, Object.<string, ol.VectorTile>>}
   */
  var tilesToDrawByZ = {};
  tilesToDrawByZ[z] = {};

  var findLoadedTiles = this.createLoadedTileFinder(source, projection,
      tilesToDrawByZ);

  var useInterimTilesOnError = layer.getUseInterimTilesOnError();

  var tmpExtent = this.tmpExtent_;
  var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
  var childTileRange, fullyLoaded, tile, tileState, x, y;
  for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {

      tile = source.getTile(z, x, y, pixelRatio, projection);
      goog.asserts.assertInstanceof(tile, ol.VectorTile,
          'Tile is an ol.VectorTile');
      tileState = tile.getState();
      if (tileState == ol.TileState.LOADED ||
          tileState == ol.TileState.EMPTY ||
          (tileState == ol.TileState.ERROR && !useInterimTilesOnError)) {
        tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
        continue;
      }

      fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
          tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
      if (!fullyLoaded) {
        childTileRange = tileGrid.getTileCoordChildTileRange(
            tile.tileCoord, tmpTileRange, tmpExtent);
        if (childTileRange) {
          findLoadedTiles(z + 1, childTileRange);
        }
      }

    }
  }

  this.dirty_ = false;

  /** @type {Array.<number>} */
  var zs = Object.keys(tilesToDrawByZ).map(Number);
  zs.sort();
  var replayables = [];
  var i, ii, currentZ, tileCoordKey, tilesToDraw;
  for (i = 0, ii = zs.length; i < ii; ++i) {
    currentZ = zs[i];
    tilesToDraw = tilesToDrawByZ[currentZ];
    for (tileCoordKey in tilesToDraw) {
      tile = tilesToDraw[tileCoordKey];
      if (tile.getState() == ol.TileState.LOADED) {
        replayables.push(tile);
        this.createReplayGroup(tile, layer, pixelRatio);
      }
    }
  }

  this.renderedTiles_ = replayables;

  return true;
};

/**
 * @param {ol.Feature|ol.render.Feature} feature Feature.
 * @param {number} squaredTolerance Squared tolerance.
 * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of
 *     styles.
 * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
 * @return {boolean} `true` if an image is loading.
 */
ol.renderer.canvas.VectorTileLayer.prototype.renderFeature =
    function(feature, squaredTolerance, styles, replayGroup) {
  if (!styles) {
    return false;
  }
  var loading = false;
  if (goog.isArray(styles)) {
    for (var i = 0, ii = styles.length; i < ii; ++i) {
      loading = ol.renderer.vector.renderFeature(
          replayGroup, feature, styles[i], squaredTolerance,
          this.handleStyleImageChange_, this) || loading;
    }
  } else {
    loading = ol.renderer.vector.renderFeature(
        replayGroup, feature, styles, squaredTolerance,
        this.handleStyleImageChange_, this) || loading;
  }
  return loading;
};
<|endoftext|>


<commit_before>var B = require('../').Buffer
var test = require('tape')

test('new buffer from array', function (t) {
  t.equal(
    new B([1, 2, 3]).toString(),
    '\u0001\u0002\u0003'
  )
  t.end()
})

test('new buffer from string', function (t) {
  t.plan(1)
  t.equal(
    new B('hey', 'utf8').toString(),
    'hey'
  )
  t.end()
})

test('new buffer from buffer', function (t) {
  var b1 = new B('asdf')
  var b2 = new B(b1)
  t.equal(b1.toString('hex'), b2.toString('hex'))
  t.end()
})

test('new buffer from uint8array', function (t) {
  if (typeof Uint8Array === 'function') {
    var b1 = new Uint8Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('buffer toArrayBuffer()', function (t) {
  var data = [1, 2, 3, 4, 5, 6, 7, 8]
  if (typeof Uint8Array === 'function') {
    var result = new B(data).toArrayBuffer()
    var expected = new Uint8Array(data).buffer
    for (var i = 0; i < expected.byteLength; i++) {
      t.equal(result[i], expected[i])
    }
  } else {
    t.pass('No toArrayBuffer() method provided in old browsers')
  }
  t.end()
})

test('buffer toJSON()', function (t) {
  t.plan(1)
  var data = [1, 2, 3, 4]
  t.deepEqual(
    new B(data).toJSON(),
    { type: 'Buffer', data: [1,2,3,4] }
  )
  t.end()
})

test('buffer copy example', function (t) {
  t.plan(1)

  buf1 = new B(26)
  buf2 = new B(26)

  for (var i = 0 ; i < 26 ; i++) {
    buf1[i] = i + 97; // 97 is ASCII a
    buf2[i] = 33; // ASCII !
  }

  buf1.copy(buf2, 8, 16, 20)

  t.equal(
    buf2.toString('ascii', 0, 25),
    '!!!!!!!!qrst!!!!!!!!!!!!!'
  )
  t.end()
})
<commit_msg>add tests for new buffer from typedarray<commit_after>var B = require('../').Buffer
var test = require('tape')

test('new buffer from array', function (t) {
  t.equal(
    new B([1, 2, 3]).toString(),
    '\u0001\u0002\u0003'
  )
  t.end()
})

test('new buffer from string', function (t) {
  t.plan(1)
  t.equal(
    new B('hey', 'utf8').toString(),
    'hey'
  )
  t.end()
})

test('new buffer from buffer', function (t) {
  var b1 = new B('asdf')
  var b2 = new B(b1)
  t.equal(b1.toString('hex'), b2.toString('hex'))
  t.end()
})

test('new buffer from uint8array', function (t) {
  if (typeof Uint8Array === 'function') {
    var b1 = new Uint8Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('new buffer from uint16array', function (t) {
  if (typeof Uint16Array === 'function') {
    var b1 = new Uint16Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('new buffer from uint32array', function (t) {
  if (typeof Uint32Array === 'function') {
    var b1 = new Uint32Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('new buffer from int16array', function (t) {
  if (typeof Int16Array === 'function') {
    var b1 = new Int16Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('new buffer from int32array', function (t) {
  if (typeof Int32Array === 'function') {
    var b1 = new Int32Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('new buffer from float32array', function (t) {
  if (typeof Float32Array === 'function') {
    var b1 = new Float32Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('new buffer from float64array', function (t) {
  if (typeof Float64Array === 'function') {
    var b1 = new Float64Array([0, 1, 2, 3])
    var b2 = new B(b1)
    t.equal(b1.length, b2.length)
    t.equal(b1[0], 0)
    t.equal(b1[1], 1)
    t.equal(b1[2], 2)
    t.equal(b1[3], 3)
    t.equal(b1[4], undefined)
  }
  t.end()
})

test('buffer toArrayBuffer()', function (t) {
  var data = [1, 2, 3, 4, 5, 6, 7, 8]
  if (typeof Uint8Array === 'function') {
    var result = new B(data).toArrayBuffer()
    var expected = new Uint8Array(data).buffer
    for (var i = 0; i < expected.byteLength; i++) {
      t.equal(result[i], expected[i])
    }
  } else {
    t.pass('No toArrayBuffer() method provided in old browsers')
  }
  t.end()
})

test('buffer toJSON()', function (t) {
  t.plan(1)
  var data = [1, 2, 3, 4]
  t.deepEqual(
    new B(data).toJSON(),
    { type: 'Buffer', data: [1,2,3,4] }
  )
  t.end()
})

test('buffer copy example', function (t) {
  t.plan(1)

  buf1 = new B(26)
  buf2 = new B(26)

  for (var i = 0 ; i < 26 ; i++) {
    buf1[i] = i + 97; // 97 is ASCII a
    buf2[i] = 33; // ASCII !
  }

  buf1.copy(buf2, 8, 16, 20)

  t.equal(
    buf2.toString('ascii', 0, 25),
    '!!!!!!!!qrst!!!!!!!!!!!!!'
  )
  t.end()
})
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, {Component} from 'react'
import {Button, Modal} from 'react-bootstrap'
import './ReportWorkflow.css'
import Settings from 'Settings'
import LinkTo from 'components/LinkTo'

import Fieldset from 'components/Fieldset'
import moment from 'moment'

const APPROVE = 'APPROVE'
const REJECT = 'REJECT'
const SUBMIT = 'SUBMIT'
const PUBLISH = 'PUBLISH'

const ACTION_TYPE_DETAILS = {
  APPROVE: {text: 'Approved', cssClass: 'btn-success approved'},
  REJECT: {text: 'Changes requested', cssClass: 'btn-danger rejected'},
  SUBMIT: {text: 'Submitted', cssClass: 'btn-pending submitted'},
  PUBLISH: {text: 'Published', cssClass: 'btn-success published'},
  null: {text: 'Pending', cssClass: 'btn-pending default'}
}

const ApprovalStepModalStatus = ({action}) => {
  if (action.type) {
    const actionType = ACTION_TYPE_DETAILS[action.type]
    const cssClass = 'label ' + actionType.cssClass
    return (
      {actionType.text} by <LinkTo person={action.person} isLink= {false} /> on
         {moment(action.createdAt).format(Settings.dateFormats.forms.withTime)}

    )
  }
  return Pending
}

class ApprovalStepModal extends Component {
  static propTypes = {
    action: PropTypes.object.isRequired,
  }

  constructor(props){
    super(props)
    this.state = {
      showModal: false
    }
  }

  closeModal = () => {
    this.setState({ showModal: false })
  }

  openModal = () => {
    this.setState({ showModal: true })
  }

  render() {
    const { action } = this.props
    const step = action.step
    const actionTypeCss =  ACTION_TYPE_DETAILS[action.type].cssClass
    return (
      step ?
        <React.Fragment>
          <Button className={actionTypeCss + ' btn-sm'} onClick={this.openModal}>
            {step.name}
          </Button>
          <Modal show={this.state.showModal} onHide={this.closeModal}>
            <Modal.Header closeButton>
                <Modal.Title>Approvers for {step.name}</Modal.Title>
            </Modal.Header>
            <Modal.Body>

              {step.approvers.map(position =>

                  <LinkTo position={position} /> - <LinkTo person={position.person} />

              )}

            </Modal.Body>
            <Modal.Footer>
              <ApprovalStepModalStatus action={action} />
            </Modal.Footer>
          </Modal>
        </React.Fragment>
      :
        null
    )
  }
}

const ActionStatus = ({action}) => {
  return
{ACTION_TYPE_DETAILS[action.type].text}

}

const ActionButton = ({action}) => {
  const step = action.step
  const actionType =  ACTION_TYPE_DETAILS[action.type]
  return (
    step ?
      <ApprovalStepModal action={action} />
    :
      <Button className={actionType.cssClass + ' btn-sm'}>
        {actionType.text}
      </Button>
  )
}

const ActionDetails = ({action}) => {
  if (action.type) {
    return(

        By <LinkTo person={action.person} />

          On {moment(action.createdAt).format(Settings.dateFormats.forms.short)}

          At {moment(action.createdAt).format('h:mm a')}

    )
  }
  return null
}

const ReportAction = ({action}) => {
  return (

      <ActionStatus action={action} />
      <ActionButton action={action} />
      <ActionDetails action={action} />

  )
}

const CompactReportAction = ({action}) => {
  return (

      <ActionButton action={action} />

  )
}

export const ReportFullWorkflow = ({report}) => {
  return (
    <Fieldset id="workflow" className="workflow-fieldset" title="Workflow">
      { report.workflow.map(action => {
        const key = action.step ? `${action.createdAt}-${action.step.uuid}` : action.createdAt
        return <ReportAction action={action} key={key} />
      })}
    </Fieldset>
  )
}

export const ReportCompactWorkflow = ({report}) => {
  return (
    <Fieldset className="workflow-fieldset compact" title="Workflow">
      { report.workflow.map(action => {
        const key = action.step ? `${action.createdAt}-${action.step.uuid}` : action.createdAt
        return <CompactReportAction action={action} key={key} />
      })}
    </Fieldset>
  )
}
<commit_msg>NCI-Agency/anet#1252: Display person on report action without approval step<commit_after>import PropTypes from 'prop-types'
import React, {Component} from 'react'
import {Button, Modal} from 'react-bootstrap'
import './ReportWorkflow.css'
import Settings from 'Settings'
import LinkTo from 'components/LinkTo'

import Fieldset from 'components/Fieldset'
import moment from 'moment'

const APPROVE = 'APPROVE'
const REJECT = 'REJECT'
const SUBMIT = 'SUBMIT'
const PUBLISH = 'PUBLISH'

const ACTION_TYPE_DETAILS = {
  APPROVE: {text: 'Approved', cssClass: 'btn-success approved'},
  REJECT: {text: 'Changes requested', cssClass: 'btn-danger rejected'},
  SUBMIT: {text: 'Submitted', cssClass: 'btn-pending submitted'},
  PUBLISH: {text: 'Published', cssClass: 'btn-success published'},
  null: {text: 'Pending', cssClass: 'btn-pending default'}
}

const ApprovalStepModalStatus = ({action}) => {
  if (action.type) {
    const actionType = ACTION_TYPE_DETAILS[action.type]
    const cssClass = 'label ' + actionType.cssClass
    return (
      {actionType.text} by <LinkTo person={action.person} isLink= {false} /> on
         {moment(action.createdAt).format(Settings.dateFormats.forms.withTime)}

    )
  }
  return Pending
}

class ApprovalStepModal extends Component {
  static propTypes = {
    action: PropTypes.object.isRequired,
  }

  constructor(props){
    super(props)
    this.state = {
      showModal: false
    }
  }

  closeModal = () => {
    this.setState({ showModal: false })
  }

  openModal = () => {
    this.setState({ showModal: true })
  }

  render() {
    const { action } = this.props
    const step = action.step
    const actionTypeCss =  ACTION_TYPE_DETAILS[action.type].cssClass
    return (
      step ?
        <React.Fragment>
          <Button className={actionTypeCss + ' btn-sm'} onClick={this.openModal}>
            {step.name}
          </Button>
          <Modal show={this.state.showModal} onHide={this.closeModal}>
            <Modal.Header closeButton>
                <Modal.Title>Approvers for {step.name}</Modal.Title>
            </Modal.Header>
            <Modal.Body>

              {step.approvers.map(position =>

                  <LinkTo position={position} /> - <LinkTo person={position.person} />

              )}

            </Modal.Body>
            <Modal.Footer>
              <ApprovalStepModalStatus action={action} />
            </Modal.Footer>
          </Modal>
        </React.Fragment>
      :
        null
    )
  }
}

const ActionStatus = ({action}) => {
  return
{ACTION_TYPE_DETAILS[action.type].text}

}

const ActionButton = ({action}) => {
  const step = action.step
  const actionType =  ACTION_TYPE_DETAILS[action.type]
  return (
    step ?
      <ApprovalStepModal action={action} />
    :
      <Button className={actionType.cssClass + ' btn-sm'}>
        <LinkTo person={action.person} isLink={false} />
      </Button>
  )
}

const ActionDetails = ({action}) => {
  if (action.type) {
    return(

        By <LinkTo person={action.person} />

          On {moment(action.createdAt).format(Settings.dateFormats.forms.short)}

          At {moment(action.createdAt).format('h:mm a')}

    )
  }
  return null
}

const ReportAction = ({action}) => {
  return (

      <ActionStatus action={action} />
      <ActionButton action={action} />
      <ActionDetails action={action} />

  )
}

const CompactReportAction = ({action}) => {
  return (

      <ActionButton action={action} />

  )
}

export const ReportFullWorkflow = ({report}) => {
  return (
    <Fieldset id="workflow" className="workflow-fieldset" title="Workflow">
      { report.workflow.map(action => {
        const key = action.step ? `${action.createdAt}-${action.step.uuid}` : action.createdAt
        return <ReportAction action={action} key={key} />
      })}
    </Fieldset>
  )
}

export const ReportCompactWorkflow = ({report}) => {
  return (
    <Fieldset className="workflow-fieldset compact" title="Workflow">
      { report.workflow.map(action => {
        const key = action.step ? `${action.createdAt}-${action.step.uuid}` : action.createdAt
        return <CompactReportAction action={action} key={key} />
      })}
    </Fieldset>
  )
}
<|endoftext|>


<commit_before>'use strict';

/***
 * Game
 * @param {number} index
 * @param {number} seed
 * @param {number} levelIndex
 * @param {Array.<string>} names
 * @param {number=} created
 * @constructor
 */
xss.Game = function(index, seed, levelIndex, names, created) {
    xss.canvas.garbageCollect();

    // Todo: Move to stage, add as netcode listener.
    if (xss.flow.stage) {
        xss.flow.stage.destruct();
    }

    xss.shapes.stage = null;
    xss.shapes.header = null;
    xss.shapes.border = null;

    this.model = new xss.ClientGameModel(created);

    /** @type {xss.Level} */
    this.level = this._setupLevel(levelIndex, seed);

    /** @type {Array.<xss.ClientSnake>} */
    this.snakes = this._spawnSnakes(names, index);

    /** @type {Array.<xss.Spawnable>} */
    this.spawnables = [];

    this._bindEvents();
};

xss.Game.prototype = {

    _bindEvents: function() {
        var ns = xss.NS_GAME;
        xss.event.on(xss.PUB_GAME_TICK,        ns, this._clientGameLoop.bind(this));
        xss.event.on(xss.EVENT_GAME_COUNTDOWN, ns, this.countdown.bind(this));
        xss.event.on(xss.EVENT_GAME_START,     ns, this.start.bind(this));
        xss.event.on(xss.EVENT_GAME_SPAWN,     ns, this._evSpawn.bind(this));
        xss.event.on(xss.EVENT_GAME_DESPAWN,   ns, this._evSpawnHit.bind(this));
        xss.event.on(xss.EVENT_SNAKE_UPDATE,   ns, this._evSnakeUpdate.bind(this));
        xss.event.on(xss.EVENT_SNAKE_SIZE,     ns, this._evSnakeSize.bind(this));
        xss.event.on(xss.EVENT_SNAKE_CRASH,    ns, this._evSnakeCrash.bind(this));
        xss.event.on(xss.EVENT_SNAKE_ACTION,   ns, this._evSnakeAction.bind(this));
        xss.event.on(xss.EVENT_SNAKE_SPEED,    ns, this._evSnakeSpeed.bind(this));
    },

    /**
     * A Game does not start immediately after a new instance of the Game class
     * is created. It will show the snakes, level, name labels and directions
     * until this function is called.
     */
    start: function() {
        xss.event.on(
            xss.PUB_WIN_FOCUS_CHANGE,
            xss.NS_GAME,
            this._handleFocus.bind(this)
        );

        for (var i = 0, m = this.snakes.length; i < m; i++) {
            this.snakes[i].removeNameAndDirection();
        }

        this.addControls();
        this.model.started = true;
    },

    destruct: function() {
        var i, m, border, ns, k;

        ns = xss.NS_GAME;

        xss.event.off(xss.PUB_GAME_TICK, ns);
        xss.event.off(xss.EVENT_GAME_COUNTDOWN, ns);
        xss.event.off(xss.EVENT_GAME_START, ns);
        xss.event.off(xss.EVENT_GAME_SPAWN, ns);
        xss.event.off(xss.EVENT_SNAKE_UPDATE, ns);
        xss.event.off(xss.EVENT_SNAKE_SIZE, ns);
        xss.event.off(xss.EVENT_SNAKE_CRASH, ns);
        xss.event.off(xss.EVENT_SNAKE_ACTION, ns);
        xss.event.off(xss.EVENT_SNAKE_SPEED, ns);

        for (i = 0, m = this.snakes.length; i < m; i++) {
            if (this.snakes[i]) {
                this.snakes[i].destruct();
            }
        }

        for (i = 0, m = this.spawnables.length; i < m; i++) {
            if (this.spawnables[i]) {
                this.spawnables[i].destruct();
            }
        }

        for (k in xss.shapes) {
            if (xss.shapes.hasOwnProperty(k)) {
                if (0 === k.indexOf(xss.NS_ANIM)) {
                    xss.shapes[k] = null;
                }
            }
        }

        border = xss.shapegen.outerBorder();
        for (k in border) {
            if (border.hasOwnProperty(k)) {
                xss.shapes[k] = null;
            }
        }

        this.spawnables = [];
        xss.shapes.level = null;
    },

    addControls: function() {
        for (var i = 0, m = this.snakes.length; i < m; i++) {
            this.snakes[i].addControls();
        }
    },

    countdown: function() {
        var from, body, dialog, updateShape, timer;

        xss.room.unbindKeys();

        from = xss.TIME_ROUND_COUNTDOWN;
        body = 'Game starting in: %d';

        dialog = new xss.Dialog('GET READY!', body.replace('%d', from));

        updateShape = function() {
            if (--from > 0) {
                dialog.setBody(body.replace('%d', from));
            } else {
                dialog.destruct();
                clearTimeout(timer);
            }
        };

        timer = window.setInterval(updateShape, 1e3);
    },

    _evSpawn: function(data) {
        var spawn, type = data[0], index = data[1];
        spawn = new xss.Spawnable(type, index, data[2]);
        this.spawnables[index] = spawn;
    },

    /**
     * @param {number} index
     */
    _evSpawnHit: function(index) {
        var spawnable = this.spawnables[index];
        if (spawnable) {
            spawnable.destruct();
            spawnable[index] = null;
        }
    },

    /**
     * @param {Array} data
     */
    _evSnakeUpdate: function(data) {
        var snake = this.snakes[data[0]];
        snake.limbo = false;
        snake.parts = data[1];
        snake.direction = data[2];
    },

    /**
     * @param {Array} data
     */
    _evSnakeSize: function(data) {
        this.snakes[data[0]].size = data[1];
    },

    /**
     * @param {Array} data
     */
    _evSnakeCrash: function(data) {
        var snake = this.snakes[data[0]];
        snake.parts = data[1];
        snake.crash();
    },

    /**
     * @param {Array} data
     */
    _evSnakeAction: function(data) {
        this.snakes[data[0]].showAction(data[1]);
    },

    /**
     * @param {Array} data
     */
    _evSnakeSpeed: function(data) {
        this.snakes[data[0]].speed = data[1];
    },

    /**
     * @param {number} index
     * @param {number} seed
     * @return {xss.Level}
     * @private
     */
    _setupLevel: function(index, seed) {
        var levelData, border;

        levelData = xss.levels.getLevelData(index);

        xss.shapes.level = xss.shapegen.level(levelData);
        xss.shapes.innerborder = xss.shapegen.innerBorder();

        border = xss.shapegen.outerBorder();
        for (var k in border) {
            if (border.hasOwnProperty(k)) {
                xss.shapes[k] = border[k];
            }
        }

        return new xss.Level(levelData, seed, this.model.offsetDelta);
    },

    /**
     * @param {Array.<string>} names
     * @param {number} index
     * @return {Array.<xss.ClientSnake>}
     * @private
     */
    _spawnSnakes: function(names, index) {
        var snakes = [];
        for (var i = 0, m = names.length; i < m; i++) {
            var snake = this._spawnSnake(i, names[i], index);
            snakes.push(snake);
        }
        return snakes;
    },

    /**
     * @param {number} i
     * @param {string} name
     * @param {number} index
     * @return {xss.ClientSnake}
     * @private
     */
    _spawnSnake: function(i, name, index) {
        var location, direction, snake;

        location = this.level.getSpawn(i);
        direction = this.level.getSpawnDirection(i);

        snake = new xss.ClientSnake(i, i === index, name, location, direction);
        snake.showName();

        if (i === index) {
            snake.showDirection();
        }

        return snake;
    },

    /**
     * @param {boolean} focus
     * @private
     */
    _handleFocus: function(focus) {
        if (focus && xss.socket) {
            xss.socket.emit(xss.EVENT_GAME_STATE);
        }
    },

    /**
     * Runs ~ every 16 ms (60 fps)
     * @param {number} delta
     * @private
     */
    _clientGameLoop: function(delta) {
        var shift, movingWalls;

        movingWalls = this.level.updateMovingWalls(delta, this.model.started);
        this._updateMovingWalls(movingWalls);

        if (this.model.started) {
            shift = this.level.levelWind.getShift(delta);
            this._moveSnakes(delta, shift);
        }
    },

    /**
     * @param {Array.<xss.ShapeCollection>} shapeCollections
     * @private
     */
    _updateMovingWalls: function(shapeCollections) {
        for (var i = 0, m = shapeCollections.length; i < m; i++) {
            if (shapeCollections[i]) {
                this._updateShapes(i, shapeCollections[i]);
            }
        }
    },

    /**
     * @param {number} index
     * @param {xss.ShapeCollection} shapeCollection
     * @private
     */
    _updateShapes: function(index, shapeCollection) {
        var shapes = shapeCollection.shapes;
        for (var i = 0, m = shapes.length; i < m; i++) {
            var key = xss.NS_ANIM + index + '_' + i;
            if (shapes[i]) {
                if (!shapes[i].cache) {
                     shapes[i].setGameTransform();
                }
                xss.shapes[key] = shapes[i];
            } else {
                xss.shapes[key] = null;
            }
        }
    },

    /**
     * @todo Move to Snake class.
     * @param {number} delta
     * @param {xss.Shift} shift
     * @private
     */
    _moveSnakes: function(delta, shift) {
        for (var i = 0, m = this.snakes.length; i < m; i++) {
            var snake = this.snakes[i];
            if (snake.elapsed >= snake.speed && !snake.crashed) {
                snake.elapsed -= snake.speed;
                this._moveSnake(snake);
            }
            snake.shiftParts(shift);
            snake.elapsed += delta;
        }
    },

    /**
     * @param {xss.ClientSnake} snake
     * @private
     */
    _moveSnake: function(snake) {
        var position = snake.getNextPosition();

        // Don't show a snake moving inside a wall, which is caused by latency.
        // Server wil update snake on whether it crashed or made a turn in time.
        if (this._isCrash(snake, position)) {
            snake.limbo = true;
        } else if (!snake.limbo) {
            snake.move(position);
            snake.updateShape();
        }
    },

    /**
     * @param {xss.ClientSnake} snake
     * @param {xss.Coordinate} position
     * @return {boolean}
     * @private
     */
    _isCrash: function(snake, position) {
        var predictSnakeParts = snake.parts.slice();
        predictSnakeParts[predictSnakeParts.length - 1] = position;

        // Walls.
        if (this._isCrashIntoWall(predictSnakeParts)) {
            return true;
        }

        // Animating walls.
        if (this._isCrashIntoAnimated(predictSnakeParts)) {
            return true;
        }

        // Own tail.
        if (snake.parts.length >= 5 && snake.hasPartPredict(position)) {
            if (snake.getPartIndex(position) !== snake.parts.length - 1) {
                return true;
            }
        }

        // Other snake.
        for (var i = 0, m = this.snakes.length; i < m; i++) {
            var opponent = this.snakes[i];
            if (opponent !== snake && opponent.hasPartPredict(position)) {
                return true;
            }
        }

        return false;
    },

    /**
     * @param {xss.SnakeParts} parts
     * @return {boolean}
     * @private
     */
    _isCrashIntoWall: function(parts) {
        for (var i = 0, m = parts.length; i < m; i++) {
            if (this.level.isWall(parts[i][0], parts[i][1])) {
                return true;
            }
        }
        return false;
    },

    /**
     * @param {xss.SnakeParts} parts
     * @return {boolean}
     * @private
     */
    _isCrashIntoAnimated: function(parts) {
        for (var i = 0, m = parts.length; i < m; i++) {
            if (this.level.isMovingWall(parts[i])) {
                return true;
            }
        }
        return false;
    }

};
<commit_msg>Skip limbo for local snake crashes<commit_after>'use strict';

/***
 * Game
 * @param {number} index
 * @param {number} seed
 * @param {number} levelIndex
 * @param {Array.<string>} names
 * @param {number=} created
 * @constructor
 */
xss.Game = function(index, seed, levelIndex, names, created) {
    xss.canvas.garbageCollect();

    // Todo: Move to stage, add as netcode listener.
    if (xss.flow.stage) {
        xss.flow.stage.destruct();
    }

    xss.shapes.stage = null;
    xss.shapes.header = null;
    xss.shapes.border = null;

    this.model = new xss.ClientGameModel(created);

    /** @type {xss.Level} */
    this.level = this._setupLevel(levelIndex, seed);

    /** @type {Array.<xss.ClientSnake>} */
    this.snakes = this._spawnSnakes(names, index);

    /** @type {Array.<xss.Spawnable>} */
    this.spawnables = [];

    this._bindEvents();
};

xss.Game.prototype = {

    _bindEvents: function() {
        var ns = xss.NS_GAME;
        xss.event.on(xss.PUB_GAME_TICK,        ns, this._clientGameLoop.bind(this));
        xss.event.on(xss.EVENT_GAME_COUNTDOWN, ns, this.countdown.bind(this));
        xss.event.on(xss.EVENT_GAME_START,     ns, this.start.bind(this));
        xss.event.on(xss.EVENT_GAME_SPAWN,     ns, this._evSpawn.bind(this));
        xss.event.on(xss.EVENT_GAME_DESPAWN,   ns, this._evSpawnHit.bind(this));
        xss.event.on(xss.EVENT_SNAKE_UPDATE,   ns, this._evSnakeUpdate.bind(this));
        xss.event.on(xss.EVENT_SNAKE_SIZE,     ns, this._evSnakeSize.bind(this));
        xss.event.on(xss.EVENT_SNAKE_CRASH,    ns, this._evSnakeCrash.bind(this));
        xss.event.on(xss.EVENT_SNAKE_ACTION,   ns, this._evSnakeAction.bind(this));
        xss.event.on(xss.EVENT_SNAKE_SPEED,    ns, this._evSnakeSpeed.bind(this));
    },

    /**
     * A Game does not start immediately after a new instance of the Game class
     * is created. It will show the snakes, level, name labels and directions
     * until this function is called.
     */
    start: function() {
        xss.event.on(
            xss.PUB_WIN_FOCUS_CHANGE,
            xss.NS_GAME,
            this._handleFocus.bind(this)
        );

        for (var i = 0, m = this.snakes.length; i < m; i++) {
            this.snakes[i].removeNameAndDirection();
        }

        this.addControls();
        this.model.started = true;
    },

    destruct: function() {
        var i, m, border, ns, k;

        ns = xss.NS_GAME;

        xss.event.off(xss.PUB_GAME_TICK, ns);
        xss.event.off(xss.EVENT_GAME_COUNTDOWN, ns);
        xss.event.off(xss.EVENT_GAME_START, ns);
        xss.event.off(xss.EVENT_GAME_SPAWN, ns);
        xss.event.off(xss.EVENT_SNAKE_UPDATE, ns);
        xss.event.off(xss.EVENT_SNAKE_SIZE, ns);
        xss.event.off(xss.EVENT_SNAKE_CRASH, ns);
        xss.event.off(xss.EVENT_SNAKE_ACTION, ns);
        xss.event.off(xss.EVENT_SNAKE_SPEED, ns);

        for (i = 0, m = this.snakes.length; i < m; i++) {
            if (this.snakes[i]) {
                this.snakes[i].destruct();
            }
        }

        for (i = 0, m = this.spawnables.length; i < m; i++) {
            if (this.spawnables[i]) {
                this.spawnables[i].destruct();
            }
        }

        for (k in xss.shapes) {
            if (xss.shapes.hasOwnProperty(k)) {
                if (0 === k.indexOf(xss.NS_ANIM)) {
                    xss.shapes[k] = null;
                }
            }
        }

        border = xss.shapegen.outerBorder();
        for (k in border) {
            if (border.hasOwnProperty(k)) {
                xss.shapes[k] = null;
            }
        }

        this.spawnables = [];
        xss.shapes.level = null;
    },

    addControls: function() {
        for (var i = 0, m = this.snakes.length; i < m; i++) {
            this.snakes[i].addControls();
        }
    },

    countdown: function() {
        var from, body, dialog, updateShape, timer;

        xss.room.unbindKeys();

        from = xss.TIME_ROUND_COUNTDOWN;
        body = 'Game starting in: %d';

        dialog = new xss.Dialog('GET READY!', body.replace('%d', from));

        updateShape = function() {
            if (--from > 0) {
                dialog.setBody(body.replace('%d', from));
            } else {
                dialog.destruct();
                clearTimeout(timer);
            }
        };

        timer = window.setInterval(updateShape, 1e3);
    },

    _evSpawn: function(data) {
        var spawn, type = data[0], index = data[1];
        spawn = new xss.Spawnable(type, index, data[2]);
        this.spawnables[index] = spawn;
    },

    /**
     * @param {number} index
     */
    _evSpawnHit: function(index) {
        var spawnable = this.spawnables[index];
        if (spawnable) {
            spawnable.destruct();
            spawnable[index] = null;
        }
    },

    /**
     * @param {Array} data
     */
    _evSnakeUpdate: function(data) {
        var snake = this.snakes[data[0]];
        snake.limbo = false;
        snake.parts = data[1];
        snake.direction = data[2];
    },

    /**
     * @param {Array} data
     */
    _evSnakeSize: function(data) {
        this.snakes[data[0]].size = data[1];
    },

    /**
     * @param {Array} data
     */
    _evSnakeCrash: function(data) {
        var snake = this.snakes[data[0]];
        snake.parts = data[1];
        snake.crash();
    },

    /**
     * @param {Array} data
     */
    _evSnakeAction: function(data) {
        this.snakes[data[0]].showAction(data[1]);
    },

    /**
     * @param {Array} data
     */
    _evSnakeSpeed: function(data) {
        this.snakes[data[0]].speed = data[1];
    },

    /**
     * @param {number} index
     * @param {number} seed
     * @return {xss.Level}
     * @private
     */
    _setupLevel: function(index, seed) {
        var levelData, border;

        levelData = xss.levels.getLevelData(index);

        xss.shapes.level = xss.shapegen.level(levelData);
        xss.shapes.innerborder = xss.shapegen.innerBorder();

        border = xss.shapegen.outerBorder();
        for (var k in border) {
            if (border.hasOwnProperty(k)) {
                xss.shapes[k] = border[k];
            }
        }

        return new xss.Level(levelData, seed, this.model.offsetDelta);
    },

    /**
     * @param {Array.<string>} names
     * @param {number} index
     * @return {Array.<xss.ClientSnake>}
     * @private
     */
    _spawnSnakes: function(names, index) {
        var snakes = [];
        for (var i = 0, m = names.length; i < m; i++) {
            var snake = this._spawnSnake(i, names[i], index);
            snakes.push(snake);
        }
        return snakes;
    },

    /**
     * @param {number} i
     * @param {string} name
     * @param {number} index
     * @return {xss.ClientSnake}
     * @private
     */
    _spawnSnake: function(i, name, index) {
        var location, direction, snake;

        location = this.level.getSpawn(i);
        direction = this.level.getSpawnDirection(i);

        snake = new xss.ClientSnake(i, i === index, name, location, direction);
        snake.showName();

        if (i === index) {
            snake.showDirection();
        }

        return snake;
    },

    /**
     * @param {boolean} focus
     * @private
     */
    _handleFocus: function(focus) {
        if (focus && xss.socket) {
            xss.socket.emit(xss.EVENT_GAME_STATE);
        }
    },

    /**
     * Runs ~ every 16 ms (60 fps)
     * @param {number} delta
     * @private
     */
    _clientGameLoop: function(delta) {
        var shift, movingWalls;

        movingWalls = this.level.updateMovingWalls(delta, this.model.started);
        this._updateMovingWalls(movingWalls);

        if (this.model.started) {
            shift = this.level.levelWind.getShift(delta);
            this._moveSnakes(delta, shift);
        }
    },

    /**
     * @param {Array.<xss.ShapeCollection>} shapeCollections
     * @private
     */
    _updateMovingWalls: function(shapeCollections) {
        for (var i = 0, m = shapeCollections.length; i < m; i++) {
            if (shapeCollections[i]) {
                this._updateShapes(i, shapeCollections[i]);
            }
        }
    },

    /**
     * @param {number} index
     * @param {xss.ShapeCollection} shapeCollection
     * @private
     */
    _updateShapes: function(index, shapeCollection) {
        var shapes = shapeCollection.shapes;
        for (var i = 0, m = shapes.length; i < m; i++) {
            var key = xss.NS_ANIM + index + '_' + i;
            if (shapes[i]) {
                if (!shapes[i].cache) {
                     shapes[i].setGameTransform();
                }
                xss.shapes[key] = shapes[i];
            } else {
                xss.shapes[key] = null;
            }
        }
    },

    /**
     * @todo Move to Snake class.
     * @param {number} delta
     * @param {xss.Shift} shift
     * @private
     */
    _moveSnakes: function(delta, shift) {
        for (var i = 0, m = this.snakes.length; i < m; i++) {
            var snake = this.snakes[i];
            if (snake.elapsed >= snake.speed && !snake.crashed) {
                snake.elapsed -= snake.speed;
                this._moveSnake(snake);
            }
            snake.shiftParts(shift);
            snake.elapsed += delta;
        }
    },

    /**
     * @param {xss.ClientSnake} snake
     * @private
     */
    _moveSnake: function(snake) {
        var position = snake.getNextPosition();

        // Don't show a snake moving inside a wall, which is caused by latency.
        // Server wil update snake on whether it crashed or made a turn in time.
        if (this._isCrash(snake, position)) {
            if (snake.local) {
                snake.crash();
            } else {
                snake.limbo = true;
            }
        } else if (!snake.limbo) {
            snake.move(position);
            snake.updateShape();
        }
    },

    /**
     * @param {xss.ClientSnake} snake
     * @param {xss.Coordinate} position
     * @return {boolean}
     * @private
     */
    _isCrash: function(snake, position) {
        var predictSnakeParts = snake.parts.slice();
        predictSnakeParts[predictSnakeParts.length - 1] = position;

        // Walls.
        if (this._isCrashIntoWall(predictSnakeParts)) {
            return true;
        }

        // Animating walls.
        if (this._isCrashIntoAnimated(predictSnakeParts)) {
            return true;
        }

        // Own tail.
        if (snake.parts.length >= 5 && snake.hasPartPredict(position)) {
            if (snake.getPartIndex(position) !== snake.parts.length - 1) {
                return true;
            }
        }

        // Other snake.
        for (var i = 0, m = this.snakes.length; i < m; i++) {
            var opponent = this.snakes[i];
            if (opponent !== snake && opponent.hasPartPredict(position)) {
                return true;
            }
        }

        return false;
    },

    /**
     * @param {xss.SnakeParts} parts
     * @return {boolean}
     * @private
     */
    _isCrashIntoWall: function(parts) {
        for (var i = 0, m = parts.length; i < m; i++) {
            if (this.level.isWall(parts[i][0], parts[i][1])) {
                return true;
            }
        }
        return false;
    },

    /**
     * @param {xss.SnakeParts} parts
     * @return {boolean}
     * @private
     */
    _isCrashIntoAnimated: function(parts) {
        for (var i = 0, m = parts.length; i < m; i++) {
            if (this.level.isMovingWall(parts[i])) {
                return true;
            }
        }
        return false;
    }

};
<|endoftext|>


<commit_before>var $ = require('jquery');
var _ = require('underscore');
var cdb = require('cartodb.js');
var PecanView = require('../../../../../javascripts/cartodb/common/dialogs/pecan/pecan_view');

describe("Pecan", function() {

  afterEach(function() {
    this.view.clean();
    this.server.restore();
  });

  beforeEach(function() {

    this.server = sinon.fakeServer.create();
    this.server.respondWith("GET", "/api/v2/sql?q=select * from (SELECT * FROM table_id) __wrap limit 0", [200, { "Content-Type": "application/json" }, '{ "response": true }']);

    window.user_data = { username: 'test' };

    spyOn($, 'ajax');

    this.table = new cdb.admin.CartoDBTableMetadata({
      id: 'table_id',
      name: 'table_name'
    });

    this.container = $('<div>').css('height', '200px');

    this.map = new cdb.geo.Map();

    this.mapView = new cdb.geo.MapView({
      el: this.container,
      map: this.map
    });

    this.fakeData = sinon.stub(this.table, 'data');

    this.fakeData.returns({
      length: 100,
      table: {
        get: function(p) {
          if (p === 'stats_geometry_types') {
            return ["ST_Point"]
          }
        }
      },
      query_schema: {
        cartodb_id: "number",
        something: "string",
        created_at: "number",
        updated_at: "number",
        the_geom: "geometry"
      }
    });

    this.user = jasmine.createSpy('cdb.admin.User');

    this.view = new PecanView({
      clean_on_hide: true,
      query_schema: this.table.data().query_schema,
      table: this.table,
      map: this.map,
      user: this.user
    });
  });

  describe("Dialog", function() {
    it("should set the base query", function() {
      this.view._start();
      expect(this.view.query).toEqual("SELECT * FROM table_id");
    });

    it("should generate a collection of columns based on the schema", function() {
      expect(this.view.columns.length).toBe(5);
      var cols = ["something", "cartodb_id", "created_at", "updated_at", "the_geom"];
      expect(_.difference(cols, this.view.columns.pluck("name")).length).toBe(0);
    });

    it("should exclude columns based on a list", function() {

      this.view._analyzeColumns();

      var rejectedColumns = this.view.columns.reject(function(column) {
        return !column.get("analyzed") && !column.get("success")
      });

      expect(this.view.columns.length).toBe(5);
      expect(rejectedColumns.length).toBe(4); // excludes 4 columns, leaves 1
    });

    it("should return ok if the table is valid", function() {
      expect(this.view._check()).toBe(true);
    });

    it("should return false if the table is not valid", function() {

      this.fakeData.restore();
      this.fakeData = sinon.stub(this.table, 'data');

      this.fakeData.returns({
        length: 0,
        table: {
          get: function(p) {
            if (p === 'stats_geometry_types') {
              return ["ST_Point"]
            }
          }
        },
        query_schema: {
          cartodb_id: "number",
          something: "string",
          created_at: "number",
          updated_at: "number",
          the_geom: "geometry"
        }
      });

      expect(this.view._check()).toBe(false);
    });
  });

  describe('date columns', function(){
    beforeEach(function() {
      var self = this;
      this.originalExecute = cdb.admin.SQL.prototype.execute;
      // Mocked response for a date column
      cdb.SQL.prototype.execute = function(sql, vars, options, callback){
        if (sql.indexOf("limit 0") > -1) {
          vars(sql, vars, options, callback);
        } else {
          var fakeResponse = '{"rows":[{"start_time":"2014-02-24T16:50:50+0100","end_time":"2015-04-18T15:18:15+0200","moments":299}],"time":0.44,"fields":{"start_time":{"type":"date"},"end_time":{"type":"date"},"moments":{"type":"number"}},"total_rows":1}';
          if(typeof vars === 'function') callback = vars;
          callback(JSON.parse(fakeResponse));
        }
      }
    });

    xit("date columns should be analyzed", function(done){
      //spyOn(cdb.admin.SQL, "describe")
      var dateColumn = new cdb.core.Model({ name: "postedtime", type: "date", geometry_type: undefined, bbox: undefined, analyzed: false });
      this.view._analyzeColumn(dateColumn);
      expect(true === false);
      /*expect($.ajax).toHaveBeenCalled();
      expect($.ajax.calls.argsFor(0)[0].url).toEqual(
        'http://' + window.user_data.username + '.localhost.lan/api/v2/sql?q=' + encodeURIComponent("select * from (" + this.view.query + ") __wrap limit 0")
      );
      expect($.ajax.calls.argsFor(0)[0].type).toEqual('get');
      expect($.ajax.calls.argsFor(0)[0].dataType).toEqual('json');*/
    })
  });
});
<commit_msg>Fix spec<commit_after>var $ = require('jquery');
var _ = require('underscore');
var cdb = require('cartodb.js');
var PecanView = require('../../../../../javascripts/cartodb/common/dialogs/pecan/pecan_view');

describe("Pecan", function() {

  afterEach(function() {
    this.view.clean();
    this.server.restore();
  });

  beforeEach(function() {

    this.server = sinon.fakeServer.create();
    this.server.respondWith("GET", "/api/v2/sql?q=select * from (SELECT * FROM table_id) __wrap limit 0", [200, { "Content-Type": "application/json" }, '{ "response": true }']);

    window.user_data = { username: 'test' };

    spyOn($, 'ajax');

    this.table = new cdb.admin.CartoDBTableMetadata({
      id: 'table_id',
      name: 'table_name'
    });

    this.container = $('<div>').css('height', '200px');

    this.map = new cdb.geo.Map();

    this.mapView = new cdb.geo.MapView({
      el: this.container,
      map: this.map
    });

    this.fakeData = sinon.stub(this.table, 'data');

    this.fakeData.returns({
      length: 100,
      table: {
        get: function(p) {
          if (p === 'stats_geometry_types') {
            return ["ST_Point"]
          }
        }
      },
      query_schema: {
        cartodb_id: "number",
        something: "string",
        created_at: "number",
        updated_at: "number",
        the_geom: "geometry"
      }
    });

    this.user = jasmine.createSpy('cdb.admin.User');

    this.view = new PecanView({
      clean_on_hide: true,
      query_schema: this.table.data().query_schema,
      table: this.table,
      map: this.map,
      user: this.user
    });
  });

  describe("Dialog", function() {
    it("should set the base query", function() {
      this.view._start();
      expect(this.view.query).toEqual("SELECT * FROM table_id");
    });

    it("should generate a collection of columns based on the schema", function() {
      expect(this.view.columns.length).toBe(5);
      var cols = ["something", "cartodb_id", "created_at", "updated_at", "the_geom"];
      expect(_.difference(cols, this.view.columns.pluck("name")).length).toBe(0);
    });

    it("should exclude columns based on a list", function() {

      this.view._analyzeColumns();

      var rejectedColumns = this.view.columns.reject(function(column) {
        return !column.get("analyzed") && !column.get("success")
      });

      expect(this.view.columns.length).toBe(4);
      expect(rejectedColumns.length).toBe(4); // excludes 3 columns, leaves 2
    });

    it("should return ok if the table is valid", function() {
      expect(this.view._check()).toBe(true);
    });

    it("should return false if the table is not valid", function() {

      this.fakeData.restore();
      this.fakeData = sinon.stub(this.table, 'data');

      this.fakeData.returns({
        length: 0,
        table: {
          get: function(p) {
            if (p === 'stats_geometry_types') {
              return ["ST_Point"]
            }
          }
        },
        query_schema: {
          cartodb_id: "number",
          something: "string",
          created_at: "number",
          updated_at: "number",
          the_geom: "geometry"
        }
      });

      expect(this.view._check()).toBe(false);
    });
  });

  describe('date columns', function(){
    beforeEach(function() {
      var self = this;
      this.originalExecute = cdb.admin.SQL.prototype.execute;
      // Mocked response for a date column
      cdb.SQL.prototype.execute = function(sql, vars, options, callback){
        if (sql.indexOf("limit 0") > -1) {
          vars(sql, vars, options, callback);
        } else {
          var fakeResponse = '{"rows":[{"start_time":"2014-02-24T16:50:50+0100","end_time":"2015-04-18T15:18:15+0200","moments":299}],"time":0.44,"fields":{"start_time":{"type":"date"},"end_time":{"type":"date"},"moments":{"type":"number"}},"total_rows":1}';
          if(typeof vars === 'function') callback = vars;
          callback(JSON.parse(fakeResponse));
        }
      }
    });

    xit("date columns should be analyzed", function(done){
      //spyOn(cdb.admin.SQL, "describe")
      var dateColumn = new cdb.core.Model({ name: "postedtime", type: "date", geometry_type: undefined, bbox: undefined, analyzed: false });
      this.view._analyzeColumn(dateColumn);
      expect(true === false);
      /*expect($.ajax).toHaveBeenCalled();
      expect($.ajax.calls.argsFor(0)[0].url).toEqual(
        'http://' + window.user_data.username + '.localhost.lan/api/v2/sql?q=' + encodeURIComponent("select * from (" + this.view.query + ") __wrap limit 0")
      );
      expect($.ajax.calls.argsFor(0)[0].type).toEqual('get');
      expect($.ajax.calls.argsFor(0)[0].dataType).toEqual('json');*/
    })
  });
});
<|endoftext|>


<commit_before>define([
	'compose',
	'ksf/base/Destroyable'
], function(
	compose,
	Destroyable
){
	var generator = function(args) {
		var getValue = args.getValue || '_getValue',
			setValue = args.setValue || '_setValue',
			parentGetValue = args.parentGetValue || 'value',
			parentPatchvalue = args.parentPatchValue || 'patch';

		var Trait = compose(Destroyable, function(parent, propName) {
			this._parent = parent;
			this._propName = propName;
		});

		Trait.prototype._extractValue = function(parentValue) {
			return parentValue && parentValue[this._propName];
		};

		Trait.prototype.value = function(value) {
			if (arguments.length === 0) {
				return this._getValue();
			} else {
				return this._setValue(value);
			}
		};

		Trait.prototype[getValue] = function() {
			if (this._destroyed) { throw "Destroyed"; }
			var self = this,
				parentValue = this._parent[parentGetValue]();
			return self._extractValue(parentValue);
		};

		Trait.prototype[setValue] = function(arg) {
			if (this._destroyed) { throw "Destroyed"; }
			return this._parent[parentPatchvalue]([{
				type: 'set',
				key: this._propName,
				value: arg
			}]);
		};

		Trait.prototype._onValue = function(listener) {
			if (this._destroyed) { throw "Destroyed"; }
			var self = this;
			return this.own(this._parent.on('value', function(parentValue) {
				listener(self._extractValue(parentValue));
			}));
		};

		Trait.prototype.on = function(event, listener) {
			if (event === 'value') {
				return this._onValue(listener);
			}
			if (event === 'changes') {
				return this._onChanges(listener);
			}
		};
		return Trait;
	};

	var Trait = generator({});
	Trait.custom = generator;
	return Trait;
});<commit_msg>PropertyAccessor's "value" event only when value has changed + "changes" event implemented<commit_after>define([
	'compose',
	'ksf/base/Destroyable'
], function(
	compose,
	Destroyable
){
	var generator = function(args) {
		var getValue = args.getValue || '_getValue',
			setValue = args.setValue || '_setValue',
			parentGetValue = args.parentGetValue || 'value',
			parentPatchvalue = args.parentPatchValue || 'patch';

		var Trait = compose(Destroyable, function(parent, propName) {
			this._parent = parent;
			this._propName = propName;
		});

		Trait.prototype._extractValue = function(parentValue) {
			return parentValue && parentValue[this._propName];
		};
		Trait.prototype._extractChanges = function(parentChanges) {
			var propName = this._propName,
				changes;
			var found = parentChanges && parentChanges.some(function(change) {
				changes = change.arg;
				return change.key === propName;
			});
			return changes;
		};

		Trait.prototype.value = function(value) {
			if (arguments.length === 0) {
				return this._getValue();
			} else {
				return this._setValue(value);
			}
		};

		Trait.prototype[getValue] = function() {
			if (this._destroyed) { throw "Destroyed"; }
			var self = this,
				parentValue = this._parent[parentGetValue]();
			return self._extractValue(parentValue);
		};

		Trait.prototype[setValue] = function(arg) {
			if (this._destroyed) { throw "Destroyed"; }
			return this._parent[parentPatchvalue]([{
				type: 'set',
				key: this._propName,
				value: arg
			}]);
		};

		Trait.prototype._onValue = function(listener) {
			if (this._destroyed) { throw "Destroyed"; }
			var self = this;
			return this.own(this._parent.on('changes', function(parentChanges) {
				var changes = self._extractChanges(parentChanges);
				changes && listener(self.value());
			}));
		};

		Trait.prototype._onChanges = function(listener) {
			if (this._destroyed) { throw "Destroyed"; }
			var self = this;
			return this.own(this._parent.on('changes', function(parentChanges) {
				var changes = self._extractChanges(parentChanges);
				changes && listener(changes);
			}));
		};

		Trait.prototype.on = function(event, listener) {
			if (event === 'value') {
				return this._onValue(listener);
			}
			if (event === 'changes') {
				return this._onChanges(listener);
			}
		};
		return Trait;
	};

	var Trait = generator({});
	Trait.custom = generator;
	return Trait;
});<|endoftext|>


<commit_before>/*
 * grunt-responsive-videos
 * https://github.com/sjwilliams/grunt-responsive-videos
 *
 * Copyright (c) 2013 Josh Williams
 * Licensed under the MIT license.
 */

'use strict';

var _ = require('underscore'),
    async = require('async'),
    path = require('path'),
    ffmpeg = require('ffmpeg-node');

module.exports = function(grunt) {

    var DEFAULT_OPTIONS = {
        separator: '-',
        sizes: [{
            name: 'small',
            width: 640
        }]
    };

    // check if there are any items in our array
    function isValidArray(obj) {
        return (_.isArray(obj) && obj.length > 0);
    }

    // check whether we've been given any valid size values
    function isValidSize(obj) {
        return (_.isNumber(obj.width) || _.isNumber(obj.height));
    }

    // create a name to suffix to our file.
    function getName(name, width, height, separator, suffix) {

        // handle empty separator as no separator
        if (typeof separator === 'undefined') {
            separator = '';
        }

        // handle empty suffix as no suffix
        if (typeof suffix === 'undefined') {
            suffix = '';
        }

        if (name) {
            return separator + name + suffix;
        } else {
            if (width && height) {
                return separator + width + 'x' + height + suffix;
            } else {
                return separator + (width || height) + suffix;
            }
        }
    }

    grunt.registerMultiTask('responsive_videos', 'Videos at various resonsive sizes', function() {

        // Merge task-specific and/or target-specific options with these defaults.
        var that = this;
        var done = this.async();
        var series = [];
        var options = this.options(DEFAULT_OPTIONS);
        var tally = {};

        if (!isValidArray(options.sizes)) {
            return grunt.fail.warn('No sizes have been defined.');
        }

        options.sizes.forEach(function(s) {

            // consts
            var DEFAULT_SIZE_OPTIONS = {
                quality: 1
            };

            // variable
            var sizeOptions = _.clone(_.extend(DEFAULT_SIZE_OPTIONS, s));
            var sizingMethod = 'resize';

            if (!isValidSize(s)) {
                return grunt.fail.warn('Size is invalid');
            }

            // use crop if both width and height are specified.
            if (s.width && s.height) {
                sizingMethod = 'crop';
            }

            // create a name suffix for our image
            sizeOptions.name = getName(s.name, s.width, s.height, options.separator, s.suffix);

            tally[sizeOptions.name] = 0;

            that.files.forEach(function(f) {

                var extName = path.extname(f.dest),
                    srcPath = f.src[0],
                    baseName = path.basename(srcPath, extName), // filename without extension
                    dirName = path.dirname(f.dest),
                    dstPath = path.join(dirName, baseName + sizeOptions.name + '.mp4');

                console.log(extName, srcPath, dirName, baseName, dstPath);

                // series.push(function(callback) {
                //     im[sizingMethod](imageOptions, function(error, stdout, stderr) {
                //         if (error) {
                //             grunt.fail.warn(error.message);
                //         } else {
                //             grunt.verbose.ok('Responsive Image: ' + srcPath + ' now ' + dstPath);
                //             tally[sizeOptions.name]++;
                //         }
                //         return callback();
                //     });

                // });

                ffmpeg.exec(['-i', srcPath, '-vcodec', 'libx264', '-acodec', 'libfaac', '-pix_fmt', 'yuv420p', '-q:v', '4',  '-q:a', '100', '-threads', '0', dstPath], function() {
                    done.apply();
                });
            });
        });
    });
};<commit_msg>default encode settings for webm and mp4<commit_after>/*
 * grunt-responsive-videos
 * https://github.com/sjwilliams/grunt-responsive-videos
 *
 * Copyright (c) 2013 Josh Williams
 * Licensed under the MIT license.
 */

'use strict';

var _ = require('underscore'),
    async = require('async'),
    path = require('path'),
    ffmpeg = require('ffmpeg-node');

module.exports = function(grunt) {

    var DEFAULT_OPTIONS = {
        separator: '-',
        sizes: [{
            name: 'small',
            width: 640,
            poster: true
        }],
        encode:[{
            webm: [
                {'-vcodec': 'libvpx'},
                {'-acodec': 'libvorbis'},
                {'-crf': '12'},
                {'-b:v': '1.5M',},
                {'-q:a': '100'},
                {'-threads': '0'}
            ],
            mp4: [
                {'-vcodec':'libx264'},
                {'-acodec': 'libfaac'},
                {'-pix_fmt': 'yuv420p'},
                {'-q:v': '4'},
                {'-q:a': '100'},
                {'-threads': '0'}
            ]
        }]
    };

    // check if there are any items in our array
    function isValidArray(obj) {
        return (_.isArray(obj) && obj.length > 0);
    }

    // check whether we've been given any valid size values
    function isValidSize(obj) {
        return (_.isNumber(obj.width) || _.isNumber(obj.height));
    }

    // create a name to suffix to our file.
    function getName(name, width, height, separator, suffix) {

        // handle empty separator as no separator
        if (typeof separator === 'undefined') {
            separator = '';
        }

        // handle empty suffix as no suffix
        if (typeof suffix === 'undefined') {
            suffix = '';
        }

        if (name) {
            return separator + name + suffix;
        } else {
            if (width && height) {
                return separator + width + 'x' + height + suffix;
            } else {
                return separator + (width || height) + suffix;
            }
        }
    }

    grunt.registerMultiTask('responsive_videos', 'Videos at various resonsive sizes', function() {

        // Merge task-specific and/or target-specific options with these defaults.
        var that = this;
        var done = this.async();
        var series = [];
        var options = this.options(DEFAULT_OPTIONS);
        var tally = {};

        if (!isValidArray(options.sizes)) {
            return grunt.fail.warn('No sizes have been defined.');
        }

        options.sizes.forEach(function(s) {

            // consts
            var DEFAULT_SIZE_OPTIONS = {
                quality: 1
            };

            // variable
            var sizeOptions = _.clone(_.extend(DEFAULT_SIZE_OPTIONS, s));
            var sizingMethod = 'resize';

            if (!isValidSize(s)) {
                return grunt.fail.warn('Size is invalid');
            }

            // use crop if both width and height are specified.
            if (s.width && s.height) {
                sizingMethod = 'crop';
            }

            // create a name suffix for our image
            sizeOptions.name = getName(s.name, s.width, s.height, options.separator, s.suffix);

            tally[sizeOptions.name] = 0;

            that.files.forEach(function(f) {

                var extName = path.extname(f.dest),
                    srcPath = f.src[0],
                    baseName = path.basename(srcPath, extName), // filename without extension
                    dirName = path.dirname(f.dest),
                    dstPath = path.join(dirName, baseName + sizeOptions.name + '.mp4');

                console.log(extName, srcPath, dirName, baseName, dstPath);

                // series.push(function(callback) {
                //     im[sizingMethod](imageOptions, function(error, stdout, stderr) {
                //         if (error) {
                //             grunt.fail.warn(error.message);
                //         } else {
                //             grunt.verbose.ok('Responsive Image: ' + srcPath + ' now ' + dstPath);
                //             tally[sizeOptions.name]++;
                //         }
                //         return callback();
                //     });

                // });

                ffmpeg.exec(['-i', srcPath, '-vcodec', 'libx264', '-acodec', 'libfaac', '-pix_fmt', 'yuv420p', '-q:v', '4',  '-q:a', '100', '-threads', '0', dstPath], function() {
                    done.apply();
                });
            });
        });
    });
};<|endoftext|>