<commit_before>"use strict";

var CityTour = CityTour || {};
CityTour.Scene = CityTour.Scene || {};

CityTour.Scene.TerrainGeometryBuilder = function() {
  var TERRAIN_COLOR_1 = new THREE.Color(0.0, 0.48, 0.0);
  var TERRAIN_COLOR_2 = new THREE.Color(0.0, 0.49, 0.0);
  var WATER_COLOR_1 = new THREE.Color(0.1, 0.2, 1.0);
  var WATER_COLOR_2 = new THREE.Color(0.1, 0.19, 1.0);

  var LAND = 'land';
  var WATER = 'water';

  var reusableTriangle = new THREE.Geometry();
  reusableTriangle.faces = [new THREE.Face3(0, 1, 2)];

  var buildTriangleGeometry = function(x1, y1, z1, x2, y2, z2, x3, y3, z3) {
    reusableTriangle.vertices = [ new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x2, y2, z2), new THREE.Vector3(x3, y3, z3) ];
    reusableTriangle.computeFaceNormals();

    return reusableTriangle;
  };

  var terrainGeometryBuilder = {};

  terrainGeometryBuilder.build = function(terrain, roadNetwork) {
    var mapX, mapZ, triangle;
    var topLeftRoad, topRightRoad, bottomLeftRoad, bottomRightRoad;
    var topLeftX, topLeftZ, topRightX, topRightZ, bottomLeftX, bottomLeftZ, bottomRightX, bottomRightZ;
    var topLeftHeight, topRightHeight, bottomLeftHeight, bottomRightHeight;
    var topLeftMaterial, topRightMaterial, bottomLeftMaterial, bottomRightMaterial;

    var halfStreetWidth = CityTour.Config.STREET_WIDTH / 2;
    var halfStreetDepth = CityTour.Config.STREET_DEPTH / 2;

    var triangleWidth = 1 / terrain.subDivisions();
    var triangleDepth = 1 / terrain.subDivisions();

    var terrainGeometry1 = new THREE.Geometry();
    var terrainGeometry2 = new THREE.Geometry();
    var waterGeometry1 = new THREE.Geometry();
    var waterGeometry2 = new THREE.Geometry();
    var terrainMaterial1 = new THREE.MeshLambertMaterial({ color: TERRAIN_COLOR_1 });
    var terrainMaterial2 = new THREE.MeshLambertMaterial({ color: TERRAIN_COLOR_2 });
    var waterMaterial1 = new THREE.MeshLambertMaterial({ color: WATER_COLOR_1 });
    var waterMaterial2 = new THREE.MeshLambertMaterial({ color: WATER_COLOR_2 });

    for (mapX = -CityTour.Config.HALF_TERRAIN_COLUMNS; mapX < CityTour.Config.HALF_TERRAIN_COLUMNS; mapX += triangleWidth) {
      for (mapZ = -CityTour.Config.HALF_TERRAIN_ROWS; mapZ < CityTour.Config.HALF_TERRAIN_ROWS; mapZ += triangleDepth) {
        topLeftRoad     = roadNetwork.hasIntersection(mapX, mapZ);
        topRightRoad    = roadNetwork.hasIntersection(mapX + triangleWidth, mapZ);
        bottomLeftRoad  = roadNetwork.hasIntersection(mapX, mapZ + triangleDepth);
        bottomRightRoad = roadNetwork.hasIntersection(mapX + triangleWidth, mapZ + triangleDepth);

        topLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        topLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topLeftRoad) {
          topLeftX += halfStreetWidth;
          topLeftZ += halfStreetDepth;
        }

        topRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        topRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topRightRoad) {
          topRightX -= halfStreetWidth;
          topRightZ += halfStreetDepth;
        }

        bottomLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        bottomLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomLeftRoad) {
          bottomLeftX += halfStreetWidth;
          bottomLeftZ -= halfStreetDepth;
        }

        bottomRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        bottomRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomRightRoad) {
          bottomRightX -= halfStreetWidth;
          bottomRightZ -= halfStreetDepth;
        }

        topLeftHeight = terrain.heightAtCoordinates(mapX, mapZ);
        topRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftHeight = terrain.heightAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        topLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ);
        topRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        // Core triangles
        triangle = buildTriangleGeometry(topLeftX,    topLeftHeight,    topLeftZ,
                                         bottomLeftX, bottomLeftHeight, bottomLeftZ,
                                         topRightX,   topRightHeight,   topRightZ);
        if (topLeftMaterial === WATER && topRightMaterial === WATER && bottomLeftMaterial === WATER) {
          waterGeometry1.merge(triangle);
        }
        else {
          terrainGeometry1.merge(triangle);
        }

        triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ,
                                         bottomRightX, bottomRightHeight, bottomRightZ,
                                         topRightX,    topRightHeight, topRightZ);
        if (bottomLeftMaterial === WATER && topRightMaterial === WATER && bottomRightMaterial === WATER) {
          waterGeometry2.merge(triangle);
        }
        else {
          terrainGeometry2.merge(triangle);;
        }

        // Extra left-side triangles
        if (topLeftRoad && !bottomLeftRoad) {
          triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, topLeftZ,
                                           CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ,
                                           topLeftX, topLeftHeight, topLeftZ);
          terrainGeometry2.merge(triangle);
        }

        if (bottomLeftRoad && !topLeftRoad) {
          triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX),  topLeftHeight,     topLeftZ,
                                           CityTour.Coordinates.mapXToSceneX(mapX),  bottomLeftHeight, bottomLeftZ,
                                           bottomLeftX, bottomLeftHeight, bottomLeftZ);
          terrainGeometry2.merge(triangle);
        }

        if (topLeftRoad && bottomLeftRoad && !roadNetwork.hasEdgeBetween(mapX, mapZ, mapX, mapZ + triangleDepth)) {
          triangle = buildTriangleGeometry(bottomLeftX - CityTour.Config.STREET_WIDTH, bottomLeftHeight, bottomLeftZ,
                                           bottomLeftX, bottomLeftHeight, bottomLeftZ,
                                           topLeftX, topLeftHeight, topLeftZ);
          terrainGeometry2.merge(triangle);
        }

        // Extra right-side triangles
        if (topRightRoad && !bottomRightRoad) {
          triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ,
                                           CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ,
                                           topRightX, topRightHeight, topRightZ);

          terrainGeometry1.merge(triangle);
        }

        if (bottomRightRoad && !topRightRoad) {
          triangle = buildTriangleGeometry(bottomRightX, bottomRightHeight, bottomRightZ,
                                           CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ,
                                           CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ);
          terrainGeometry1.merge(triangle);
        }

        if (topRightRoad && bottomRightRoad && !roadNetwork.hasEdgeBetween(mapX + triangleWidth, mapZ, mapX + triangleWidth, mapZ + triangleDepth)) {
          triangle = buildTriangleGeometry(topRightX,    topRightHeight, topRightZ,
                                           bottomRightX, bottomRightHeight, bottomRightZ,
                                           topRightX + CityTour.Config.STREET_WIDTH, topRightHeight, topRightZ);
          terrainGeometry1.merge(triangle);
        }

        // Extra top-side triangles
        if (topLeftRoad && !topRightRoad) {
          triangle = buildTriangleGeometry(topLeftX,  topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ),
                                           topLeftX,  topLeftHeight, topLeftZ,
                                           CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ));
          terrainGeometry2.merge(triangle);
        }

        if (topRightRoad && !topLeftRoad) {
          triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ),
                                           topRightX, topRightHeight, topRightZ,
                                           topRightX, topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ));
          terrainGeometry2.merge(triangle);
        }

        if (topLeftRoad && topRightRoad && !roadNetwork.hasEdgeBetween(mapX, mapZ, mapX + triangleWidth, mapZ)) {
          triangle = buildTriangleGeometry(topLeftX, topLeftHeight, topLeftZ,
                                           topRightX, topRightHeight, topRightZ,
                                           topRightX, topRightHeight, topRightZ - CityTour.Config.STREET_DEPTH);

          terrainGeometry2.merge(triangle);
        }

        // Extra bottom-side triangles
        if (bottomLeftRoad && !bottomRightRoad) {
          triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ,
                                           bottomLeftX,  bottomLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth),
                                           bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth));
          terrainGeometry1.merge(triangle);
        }

        if (bottomRightRoad && !bottomLeftRoad) {
          triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ,
                                           bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth),
                                           bottomRightX, bottomRightHeight, bottomRightZ);
          terrainGeometry1.merge(triangle);
        }

        if (bottomLeftRoad && bottomRightRoad && !roadNetwork.hasEdgeBetween(mapX, mapZ + 1, mapX + triangleWidth, mapZ + triangleDepth)) {
          triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ,
                                           bottomLeftX,  bottomLeftHeight, bottomLeftZ + CityTour.Config.STREET_DEPTH,
                                           bottomRightX, bottomRightHeight, bottomRightZ);

          terrainGeometry1.merge(triangle);
        }
      }
    }

    var mesh1 = new THREE.Mesh(terrainGeometry1, terrainMaterial1);
    var mesh2 = new THREE.Mesh(terrainGeometry2, terrainMaterial2);
    var waterMesh1 = new THREE.Mesh(waterGeometry1, waterMaterial1);
    var waterMesh2 = new THREE.Mesh(waterGeometry2, waterMaterial2);

    return [mesh1, mesh2, waterMesh1, waterMesh2];
  };

  return terrainGeometryBuilder;
};
<commit_msg>Fixing terrain geometry generation for high resolution terrain<commit_after>"use strict";

var CityTour = CityTour || {};
CityTour.Scene = CityTour.Scene || {};

CityTour.Scene.TerrainGeometryBuilder = function() {
  var TERRAIN_COLOR_1 = new THREE.Color(0.0, 0.48, 0.0);
  var TERRAIN_COLOR_2 = new THREE.Color(0.0, 0.49, 0.0);
  var WATER_COLOR_1 = new THREE.Color(0.1, 0.2, 1.0);
  var WATER_COLOR_2 = new THREE.Color(0.1, 0.19, 1.0);

  var LAND = 'land';
  var WATER = 'water';

  var reusableTriangle = new THREE.Geometry();
  reusableTriangle.faces = [new THREE.Face3(0, 1, 2)];

  var buildTriangleGeometry = function(x1, y1, z1, x2, y2, z2, x3, y3, z3) {
    reusableTriangle.vertices = [ new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x2, y2, z2), new THREE.Vector3(x3, y3, z3) ];
    reusableTriangle.computeFaceNormals();

    return reusableTriangle;
  };

  var terrainGeometryBuilder = {};

  terrainGeometryBuilder.build = function(terrain, roadNetwork) {
    var mapX, mapZ, triangle;
    var leftRoad, topRoad, bottomRoad, rightRoad;
    var topLeftRoad, topRightRoad, bottomLeftRoad, bottomRightRoad;
    var topLeftX, topLeftZ, topRightX, topRightZ, bottomLeftX, bottomLeftZ, bottomRightX, bottomRightZ;
    var topLeftHeight, topRightHeight, bottomLeftHeight, bottomRightHeight;
    var topLeftMaterial, topRightMaterial, bottomLeftMaterial, bottomRightMaterial;

    var halfStreetWidth = CityTour.Config.STREET_WIDTH / 2;
    var halfStreetDepth = CityTour.Config.STREET_DEPTH / 2;

    var triangleWidth = 1 / terrain.subDivisions();
    var triangleDepth = 1 / terrain.subDivisions();

    var terrainGeometry1 = new THREE.Geometry();
    var terrainGeometry2 = new THREE.Geometry();
    var waterGeometry1 = new THREE.Geometry();
    var waterGeometry2 = new THREE.Geometry();
    var terrainMaterial1 = new THREE.MeshLambertMaterial({ color: TERRAIN_COLOR_1 });
    var terrainMaterial2 = new THREE.MeshLambertMaterial({ color: TERRAIN_COLOR_2 });
    var waterMaterial1 = new THREE.MeshLambertMaterial({ color: WATER_COLOR_1 });
    var waterMaterial2 = new THREE.MeshLambertMaterial({ color: WATER_COLOR_2 });

    for (mapX = -CityTour.Config.HALF_TERRAIN_COLUMNS; mapX < CityTour.Config.HALF_TERRAIN_COLUMNS; mapX += triangleWidth) {
      for (mapZ = -CityTour.Config.HALF_TERRAIN_ROWS; mapZ < CityTour.Config.HALF_TERRAIN_ROWS; mapZ += triangleDepth) {
        topLeftRoad     = roadNetwork.hasIntersection(mapX, mapZ);
        topRightRoad    = roadNetwork.hasIntersection(mapX + triangleWidth, mapZ);
        bottomLeftRoad  = roadNetwork.hasIntersection(mapX, mapZ + triangleDepth);
        bottomRightRoad = roadNetwork.hasIntersection(mapX + triangleWidth, mapZ + triangleDepth);

        leftRoad = (Math.floor(mapX) === mapX) && roadNetwork.hasEdgeBetween(mapX, Math.floor(mapZ), mapX, Math.floor(mapZ) + 1);
        rightRoad = (Math.ceil(mapX + triangleWidth) === (mapX + triangleWidth)) && roadNetwork.hasEdgeBetween(mapX + triangleWidth, Math.floor(mapZ), mapX + triangleWidth, Math.floor(mapZ) + 1);
        topRoad = (Math.floor(mapZ) === mapZ) && roadNetwork.hasEdgeBetween(Math.floor(mapX), mapZ, Math.floor(mapX) + 1, mapZ);
        bottomRoad = (Math.ceil(mapZ + triangleDepth) === (mapZ + triangleDepth)) && roadNetwork.hasEdgeBetween(Math.floor(mapX), mapZ + triangleDepth, Math.floor(mapX) + 1, mapZ + triangleDepth);

        topLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        topLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topLeftRoad || leftRoad) {
          topLeftX += halfStreetWidth;
        }
        if (topLeftRoad || topRoad) {
          topLeftZ += halfStreetDepth;
        }

        topRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        topRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topRightRoad || rightRoad) {
          topRightX -= halfStreetWidth;
        }
        if (topRightRoad || topRoad) {
          topRightZ += halfStreetDepth;
        }

        bottomLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        bottomLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomLeftRoad || leftRoad) {
          bottomLeftX += halfStreetWidth;
        }
        if (bottomLeftRoad || bottomRoad) {
          bottomLeftZ -= halfStreetDepth;
        }

        bottomRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        bottomRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomRightRoad || rightRoad) {
          bottomRightX -= halfStreetWidth;
        }
        if (bottomRightRoad || bottomRoad) {
          bottomRightZ -= halfStreetDepth;
        }

        topLeftHeight = terrain.heightAtCoordinates(mapX, mapZ);
        topRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftHeight = terrain.heightAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        topLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ);
        topRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        // Core triangles
        triangle = buildTriangleGeometry(topLeftX,    topLeftHeight,    topLeftZ,
                                         bottomLeftX, bottomLeftHeight, bottomLeftZ,
                                         topRightX,   topRightHeight,   topRightZ);
        if (topLeftMaterial === WATER && topRightMaterial === WATER && bottomLeftMaterial === WATER) {
          waterGeometry1.merge(triangle);
        }
        else {
          terrainGeometry1.merge(triangle);
        }

        triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ,
                                         bottomRightX, bottomRightHeight, bottomRightZ,
                                         topRightX,    topRightHeight, topRightZ);
        if (bottomLeftMaterial === WATER && topRightMaterial === WATER && bottomRightMaterial === WATER) {
          waterGeometry2.merge(triangle);
        }
        else {
          terrainGeometry2.merge(triangle);
        }

        // Extra left-side triangles
        if (!leftRoad) {
          if (topLeftRoad && !bottomLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, topLeftZ,
                                             CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ,
                                             topLeftX, topLeftHeight, topLeftZ);
            terrainGeometry2.merge(triangle);
          }

          if (bottomLeftRoad && !topLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX),  topLeftHeight,     topLeftZ,
                                             CityTour.Coordinates.mapXToSceneX(mapX),  bottomLeftHeight, bottomLeftZ,
                                             bottomLeftX, bottomLeftHeight, bottomLeftZ);
            terrainGeometry2.merge(triangle);
          }

          if (topLeftRoad && bottomLeftRoad) {
            triangle = buildTriangleGeometry(bottomLeftX - CityTour.Config.STREET_WIDTH, bottomLeftHeight, bottomLeftZ,
                                             bottomLeftX, bottomLeftHeight, bottomLeftZ,
                                             topLeftX, topLeftHeight, topLeftZ);
            terrainGeometry2.merge(triangle);
          }
        }

        // Extra right-side triangles
        if (!rightRoad) {
          if (topRightRoad && !bottomRightRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ,
                                             topRightX, topRightHeight, topRightZ);

            terrainGeometry1.merge(triangle);
          }

          if (bottomRightRoad && !topRightRoad) {
            triangle = buildTriangleGeometry(bottomRightX, bottomRightHeight, bottomRightZ,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ);
            terrainGeometry1.merge(triangle);
          }

          if (topRightRoad && bottomRightRoad) {
            triangle = buildTriangleGeometry(topRightX,    topRightHeight, topRightZ,
                                             bottomRightX, bottomRightHeight, bottomRightZ,
                                             topRightX + CityTour.Config.STREET_WIDTH, topRightHeight, topRightZ);
            terrainGeometry1.merge(triangle);
          }
        }

        // Extra top-side triangles
        if (!topRoad) {
          if (topLeftRoad && !topRightRoad) {
            triangle = buildTriangleGeometry(topLeftX,  topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ),
                                             topLeftX,  topLeftHeight, topLeftZ,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ));
            terrainGeometry2.merge(triangle);
          }

          if (topRightRoad && !topLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ),
                                             topRightX, topRightHeight, topRightZ,
                                             topRightX, topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ));
            terrainGeometry2.merge(triangle);
          }

          if (topLeftRoad && topRightRoad) {
            triangle = buildTriangleGeometry(topLeftX, topLeftHeight, topLeftZ,
                                             topRightX, topRightHeight, topRightZ,
                                             topRightX, topRightHeight, topRightZ - CityTour.Config.STREET_DEPTH);

            terrainGeometry2.merge(triangle);
          }
        }

        // Extra bottom-side triangles
        if (!bottomRoad) {
          if (bottomLeftRoad && !bottomRightRoad) {
            triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ,
                                             bottomLeftX,  bottomLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth),
                                             bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth));
            terrainGeometry1.merge(triangle);
          }

          if (bottomRightRoad && !bottomLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ,
                                             bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth),
                                             bottomRightX, bottomRightHeight, bottomRightZ);
            terrainGeometry1.merge(triangle);
          }

          if (bottomLeftRoad && bottomRightRoad) {
            triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ,
                                             bottomLeftX,  bottomLeftHeight, bottomLeftZ + CityTour.Config.STREET_DEPTH,
                                             bottomRightX, bottomRightHeight, bottomRightZ);

            terrainGeometry1.merge(triangle);
          }
        }

      }
    }

    var mesh1 = new THREE.Mesh(terrainGeometry1, terrainMaterial1);
    var mesh2 = new THREE.Mesh(terrainGeometry2, terrainMaterial2);
    var waterMesh1 = new THREE.Mesh(waterGeometry1, waterMaterial1);
    var waterMesh2 = new THREE.Mesh(waterGeometry2, waterMaterial2);

    return [mesh1, mesh2, waterMesh1, waterMesh2];
  };

  return terrainGeometryBuilder;
};
<|endoftext|>


<commit_before>// Import the neccesary modules.
import asyncq from 'async-q';
import { ItemType } from 'butter-provider';

import FactoryProducer from '../resources/FactoryProducer';
import Provider from './Provider';

/**
 * Class for scraping content from various sources.
 * @implements {Provider}
 */
export default class BaseProvider extends Provider {

  /**
   * The types of models available for the API.
   * @type {Object}
   */
  static ModelTypes = {
    AnimeMovie: 'animemovie',
    AnimeShow: 'animeshow',
    Movie: 'movie',
    Show: 'show'
  }

  /**
   * The types of content available for the API.
   * @type {Object}
   */
  static Types = {
    Movie: ItemType.MOVIE,
    Show: ItemType.TVSHOW
  }

  /**
   * The maximum web requests can take place at the same time. Default is `2`.
   * @type {Number}
   */
  _maxWebRequest = 2;

  /**
   * Create a BulkProvider class.
   * @param {Object} config - The configuration object for the torrent
   * provider.
   * @param {Object} config.api - The name of api for the torrent provider.
   * @param {String} config.name - The name of the torrent provider.
   * @param {String} config.modelType - The model type for the helper.
   * @param {Object} config.query - The query object for the api.
   * @param {String} config.type - The type of content to scrape.
   */
  constructor({api, name, modelType, query, type} = {}) {
    super();

    const apiFactory = FactoryProducer.getFactory('api');
    const helperFactory = FactoryProducer.getFactory('helper');
    const modelFactory = FactoryProducer.getFactory('model');

    const model = modelFactory.getModel(modelType);

    /**
     * The api of the torrent provider.
     * @type {Object}
     */
    this._api = apiFactory.getApi(api);

    /**
     * The name of the torrent provider.
     * @type {String}
     */
    this._name = name;

    /**
     * The helper class for adding movies.
     * @type {BaseHelper}
     */
    this._helper = helperFactory.getHelper(this._name, model, type);

    /**
     * The query object for the api.
     * @type {Object}
     */
    this._query = query;

    /**
     * The type of content to scrape.
     * @type {String}
     */
    this._type = type;
  }

  /**
   * Get all the content.
   * @override
   * @param {Object} content - The content information.
   * @returns {Object} - A content object.
   */
  async getContent(content) {
    try {
      let newContent;

      if (this._type === ItemType.MOVIE) {
        const { slugYear, torrents } = content;
        newContent = await this._helper.getTraktInfo(slugYear);

        if (newContent && newContent._id)
          return await this._helper.addTorrents(newContent, torrents);
      } else if (this._type === ItemType.TVSHOW) {
        const { episodes, slug } = content;
        delete episodes[0];
        newContent = await this._helper.getTraktInfo(slug);

        if (newContent && newContent._id)
          return await this._helper.addEpisodes(newContent, episodes, slug);
      } else {
        return logger.error(`'${this._type}' is not a valid value for ItemType!`);
      }
    } catch (err) {
      return logger.error(err);
    }
  }

  /**
   * Get all the torrents of a given torrent provider.
   * @override
   * @param {Number} totalPages - The total pages of the query.
   * @returns {Array<Object>} - A list of all the queried torrents.
   */
  _getAllTorrents(totalPages) {
    let torrents = [];
    return asyncq.timesSeries(totalPages, async page => {
      if (this._query.page) this._query.page = page + 1;
      if (this._query.offset) this._query.offset = page + 1;

      logger.info(`${this._name}: Starting searching ${this._name} on page ${page + 1} out of ${totalPages}`);
      const result = await this._api.search(this._query);
      torrents = torrents.concat(result.results);
    }).then(() => {
      logger.info(`${this._name}: Found ${torrents.length} torrents.`);
      return torrents;
    });
  }

  /**
   * Returns a list of all the inserted torrents.
   * @override
   * @returns {Array<Object>} - A list of scraped content.
   */
  async search() {
    try {
      const getTotalPages = await this._api.search(this._query);

      let totalPages;
      if (process.env.NODE_ENV === 'development') {
        totalPages = 1;
      } else {
        totalPages = getTotalPages.total_pages
                              ? getTotalPages.total_pages
                              : Math.ceil(getTotalPages.data.movie_count / 50);
      }

      if (!totalPages)
        return logger.error(`${this._name}: totalPages returned: '${totalPages}'`);

      logger.info(`${this._name}: Total pages ${totalPages}`);

      const torrents = await this._getAllTorrents(totalPages);
      const allContent = await this._getAllContent(torrents, this._query.language);

      return await asyncq.mapLimit(allContent, BaseProvider._maxWebRequest,
        content => this.getContent(content)
      );
    } catch (err) {
      return logger.error(err);
    }
  }

}
<commit_msg>_maxWebRequest made static<commit_after>// Import the neccesary modules.
import asyncq from 'async-q';
import { ItemType } from 'butter-provider';

import FactoryProducer from '../resources/FactoryProducer';
import Provider from './Provider';

/**
 * Class for scraping content from various sources.
 * @implements {Provider}
 */
export default class BaseProvider extends Provider {

  /**
   * The types of models available for the API.
   * @type {Object}
   */
  static ModelTypes = {
    AnimeMovie: 'animemovie',
    AnimeShow: 'animeshow',
    Movie: 'movie',
    Show: 'show'
  }

  /**
   * The types of content available for the API.
   * @type {Object}
   */
  static Types = {
    Movie: ItemType.MOVIE,
    Show: ItemType.TVSHOW
  }

  /**
   * The maximum web requests can take place at the same time. Default is `2`.
   * @type {Number}
   */
  static _maxWebRequest = 2;

  /**
   * Create a BulkProvider class.
   * @param {Object} config - The configuration object for the torrent
   * provider.
   * @param {Object} config.api - The name of api for the torrent provider.
   * @param {String} config.name - The name of the torrent provider.
   * @param {String} config.modelType - The model type for the helper.
   * @param {Object} config.query - The query object for the api.
   * @param {String} config.type - The type of content to scrape.
   */
  constructor({api, name, modelType, query, type} = {}) {
    super();

    const apiFactory = FactoryProducer.getFactory('api');
    const helperFactory = FactoryProducer.getFactory('helper');
    const modelFactory = FactoryProducer.getFactory('model');

    const model = modelFactory.getModel(modelType);

    /**
     * The api of the torrent provider.
     * @type {Object}
     */
    this._api = apiFactory.getApi(api);

    /**
     * The name of the torrent provider.
     * @type {String}
     */
    this._name = name;

    /**
     * The helper class for adding movies.
     * @type {BaseHelper}
     */
    this._helper = helperFactory.getHelper(this._name, model, type);

    /**
     * The query object for the api.
     * @type {Object}
     */
    this._query = query;

    /**
     * The type of content to scrape.
     * @type {String}
     */
    this._type = type;
  }

  /**
   * Get all the content.
   * @override
   * @param {Object} content - The content information.
   * @returns {Object} - A content object.
   */
  async getContent(content) {
    try {
      let newContent;

      if (this._type === ItemType.MOVIE) {
        const { slugYear, torrents } = content;
        newContent = await this._helper.getTraktInfo(slugYear);

        if (newContent && newContent._id)
          return await this._helper.addTorrents(newContent, torrents);
      } else if (this._type === ItemType.TVSHOW) {
        const { episodes, slug } = content;
        delete episodes[0];
        newContent = await this._helper.getTraktInfo(slug);

        if (newContent && newContent._id)
          return await this._helper.addEpisodes(newContent, episodes, slug);
      } else {
        return logger.error(`'${this._type}' is not a valid value for ItemType!`);
      }
    } catch (err) {
      return logger.error(err);
    }
  }

  /**
   * Get all the torrents of a given torrent provider.
   * @override
   * @param {Number} totalPages - The total pages of the query.
   * @returns {Array<Object>} - A list of all the queried torrents.
   */
  _getAllTorrents(totalPages) {
    let torrents = [];
    return asyncq.timesSeries(totalPages, async page => {
      if (this._query.page) this._query.page = page + 1;
      if (this._query.offset) this._query.offset = page + 1;

      logger.info(`${this._name}: Starting searching ${this._name} on page ${page + 1} out of ${totalPages}`);
      const result = await this._api.search(this._query);
      torrents = torrents.concat(result.results);
    }).then(() => {
      logger.info(`${this._name}: Found ${torrents.length} torrents.`);
      return torrents;
    });
  }

  /**
   * Returns a list of all the inserted torrents.
   * @override
   * @returns {Array<Object>} - A list of scraped content.
   */
  async search() {
    try {
      const getTotalPages = await this._api.search(this._query);

      let totalPages;
      if (process.env.NODE_ENV === 'development') {
        totalPages = 1;
      } else {
        totalPages = getTotalPages.total_pages
                              ? getTotalPages.total_pages
                              : Math.ceil(getTotalPages.data.movie_count / 50);
      }

      if (!totalPages)
        return logger.error(`${this._name}: totalPages returned: '${totalPages}'`);

      logger.info(`${this._name}: Total pages ${totalPages}`);

      const torrents = await this._getAllTorrents(totalPages);
      const allContent = await this._getAllContent(torrents, this._query.language);

      return await asyncq.mapLimit(allContent, BaseProvider._maxWebRequest,
        content => this.getContent(content)
      );
    } catch (err) {
      return logger.error(err);
    }
  }

}
<|endoftext|>


<commit_before>//Connectors
exports.SockJsClientConnector = require('./connectors/SockJsClientConnector');
exports.SocketioClientConnector = require('./connectors/SocketioClientConnector');
exports.ApeInlinePushConnector = require('./connectors/ApeInlinePushConnector');
exports.RestPushConnector = require('./connectors/RestPushConnector');

//Auth
exports.TokenAuthenticator = require('./auth/TokenAuthenticator');
exports.AlwaysAcceptAuthenticator = require('./auth/AlwaysAcceptAuthenticator');

exports.Dispatcher = require('./models/Dispatcher');

exports.ConnectionManager = require('./models/ConnectionManager');
exports.UserManager = require('./models/UserManager');
exports.ChannelManager = require('./models/ChannelManager');

<commit_msg>Disable SockJS<commit_after>//Connectors
//exports.SockJsClientConnector = require('./connectors/SockJsClientConnector');
exports.SocketioClientConnector = require('./connectors/SocketioClientConnector');
//exports.ApeInlinePushConnector = require('./connectors/ApeInlinePushConnector');
exports.RestPushConnector = require('./connectors/RestPushConnector');

//Auth
exports.TokenAuthenticator = require('./auth/TokenAuthenticator');
exports.AlwaysAcceptAuthenticator = require('./auth/AlwaysAcceptAuthenticator');

exports.Dispatcher = require('./models/Dispatcher');

exports.ConnectionManager = require('./models/ConnectionManager');
exports.UserManager = require('./models/UserManager');
exports.ChannelManager = require('./models/ChannelManager');

<|endoftext|>


<commit_before>/**
 * @ngdoc service
 * @name $animate
 * @function
 *
 * @description
 * The Famo.us/Angular implementation of the `$animate` service provides Famo.us animation support for
 * Angular's core enter, leave, and move structural events.
 *
 * With the attributes `fa-animate-enter`, `fa-animate-leave`, `fa-animate-move`, you can assign an arbitrary
 * expression to animation events.
 *
 * To notify Famo.us/Angular when your animations are complete, you must do one of two things:
 * either pass a `$done` callback in your animation expressions, or design your animation expressions to
 * evaluate as the numeric duration, in milliseconds, of the animation. If an animation expression
 * both evaluates as a non-number and fails to invoke the `$done` callback, the animation event pipeline
 * will not resolve correctly and items will fail to enter, leave, and move correctly.
 *
 * To inform Famo.us/Angular how to halt any in-progress animation, use the `fa-animate-halt` attribute.
 *
 * The core Angular animation API is fundamentally CSS class-based. Because only Famo.us Surfaces
 * support CSS classes, core directives such as `ngClass`, `ngShow`, `ngIf`, and others should be applied
 * only with directives representing Surfaces (such as {@link faSurface faSurface} and
 * {@link faImageSurface faImageSurface}).
 *
 * The {@link https://docs.angularjs.org/api/ngAnimate ngAnimate} module's documentation lists the set of
 * core directives supporting $animate events. Please note that the `ngAnimate` module is *not* required
 * (or recommended) to implement $animate events with Famo.us, but it is compatible and technically effective
 * on Surfaces.
 *
 * @usage
 * ```html
 * <ANY
 *   fa-animate-enter="expression"
 *   fa-animate-leave="expression"
 *   fa-animate-move="expression"
 *   fa-animate-halt="expression"
 *   ...
 * >
 * </ANY>
 * ```
 * @example
 * ```html
 * <fa-modifier
 *   ng-repeat="item in items"
 *   fa-rotate-y="transitionable.get()"
 *   fa-animate-enter="enter()"
 *   fa-animate-leave="leave($done)"
 *   fa-animate-halt="halt()"
 * >
 *   ...
 * </fa-modifier>
 * ```
 * ```javascript
 * var Transitionable = $famous['famous/transitions/Transitionable'];
 * var SnapTransition = $famous['famous/transitions/SnapTransition'];
 * var DURATION = 500;
 *
 * $scope.transitionable = new Transitionable(Math.PI / 4);
 *
 * // Fold items down to the right when they enter.
 * $scope.enter = function() {
 *   scope.transitionable.set(
 *     0,
 *     {
 *       method: SnapTransition,
 *       duration: DURATION
 *     }
 *   );
 *
 *  // Declare the animation duration by returning it as a number
 *  return DURATION;
 * };
 *
 * // Fold items up to the left when they leave.
 * $scope.leave = function(done) {
 *   scope.transitionable.set(
 *     Math.PI / 2,
 *     {
 *       method: SnapTransition,
 *       duration: DURATION
 *     },
 *     // Execute the done callback after the transition is fully applied
 *     done
 *   );
 * };
 *
 * scope.halt = function() {
 *   // Halt any active animations
 *   scope.transitionable.halt();
 * };
 * ```
 */
angular.module('famous.angular')
  .config(['$provide', function($provide) {
    // Hook into the animation system to emit ng-class syncers to surfaces
    $provide.decorator('$animate', ['$delegate', '$rootScope', '$famous', '$parse',
                            function($delegate,   $rootScope,   $famous,   $parse) {

      var Timer   = $famous['famous/utilities/Timer'];

      var FA_ANIMATION_ACTIVE = '$$faAnimationActive';

      /**
       * Pass through $animate methods that are strictly class based.
       * These will work on Surfaces, and will be ignored elsewhere.
       * ngAnimate has a complex API for determining when an animation should be
       * considered "enabled" which we do not need.
       */
      var animationHandlers = {
        enabled: $delegate.enabledß
      };

      angular.forEach(['addClass', 'removeClass'], function(classManipulator) {
        // Stash the original class manipulator so we can apply it later
        var originalManipulator = angular.element.prototype[classManipulator];

        /**
         * Fork the angular.element.prototype class manipulator to delegate class changes
         * down to any transcluded Surfaces created by Famo.us/Angular.
         * @param  {String} className - the class to be added or removed
         * @return {void}
         */
        angular.element.prototype[classManipulator] = function(className) {
          originalManipulator.apply(this, arguments);

          // If and only if the current element represents a Famo.us Surface,
          // AND the class is not an empty string, pass through
          // the addClass and removeClass methods to the underlying renderNode.
          if ($famous.util.isASurface(this) && typeof className === 'string' && className.trim() !== '') {
            $famous.getIsolate(this.scope()).renderNode[classManipulator](className);
          }
          return this;
        };

        /**
         * Core Angular animation events will add and remove classes (such as ng-hide)
         * to affect the display of elements using $animate.addClass. Because Famo.us
         * Surfaces make use of classes, we should pass all class-based modifications
         * directively to their Surfaces whenever possible.
         */
        animationHandlers[classManipulator] = function(element, className, done) {

          $delegate[classManipulator](element, className, done);
          if($famous.util.isFaElement(element)){
            var isolate = $famous.getIsolate(element.scope());
            if ($famous.util.isASurface(element)) {

              var surface = isolate.renderNode;
              angular.forEach(className.split(' '), function(splitClassName) {
                if(splitClassName === 'ng-hide'){
                  if(classManipulator === 'addClass'){
                    isolate.hide();
                  } else if( classManipulator === 'removeClass' ){
                    isolate.show();
                  }
                }else {

                  surface[classManipulator](splitClassName);
                }
              });
            } else {
              angular.forEach(className.split(' '), function(splitClassName) {
                if(splitClassName === 'ng-hide'){
                  if(classManipulator === 'addClass'){
                    isolate.hide();
                  } else if( classManipulator === 'removeClass' ){
                    isolate.show();
                  }
                }
              });
            }
          }
         };
      });

      // There isn't a good way to delegate down to Surface.setClasses
      // because Angular has already negotiated the list of items to add
      // and items to remove. Manually loop through both lists.
      animationHandlers.setClass = function(element, add, remove, done) {

        $delegate.setClass(element, add, remove, done);

        if ($famous.util.isASurface(element)) {
          var surface = $famous.getIsolate(element.scope()).renderNode;
          angular.forEach(add.split(' '), function(className) {
            surface.addClass(className);
          });

          angular.forEach(remove.split(' '), function(className) {
            surface.removeClass(className);
          });
        }
      };

      /**
       * $animate.enter, $animate.leave, and $animate.move events
       * can trigger Famo.us animations with the `fa-animate-enter`,
       * `fa-animate-move`, and `fa-animate-leave` attributes.
       * If defined, each of these properties should evaluate to expressions
       * that equal the duration of their animations in milliseconds.
       *
       * Explicitly declaring the duration of all animations ensures
       * Famo.us will know how long to wait before considering the animation
       * complete and allow Angular to continue manipulating elements and classes.
       */
      angular.forEach(['enter', 'leave', 'move'], function(operation) {
        animationHandlers[operation] = function(element) {
          var self = this;
          var selfArgs = arguments;
          var delegateFirst = (operation === 'enter');

          if (delegateFirst === true) {
             $delegate[operation].apply(this, arguments);
          }

           // Detect if an animation is currently running
          if (element.data(FA_ANIMATION_ACTIVE) === true) {
            $parse(element.attr('fa-animate-halt'))(element.scope());
          }

          // Indicate an animation is currently running
          element.data(FA_ANIMATION_ACTIVE, true);

          var doneCallback = function() {

            var scopeId = element.scope() && element.scope().$id;

            //hide the element on animate.leave
            if(operation === 'leave' && $famous.util.isFaElement(element)){
              var isolate = $famous.getIsolate(element.scope());
              if(isolate && isolate.id) isolate.hide();
             }
            // Abort if the done callback has already been invoked
            if (element.data(FA_ANIMATION_ACTIVE) === false) {
              return;
            }

            // Indicate an animation is no longer running
            element.data(FA_ANIMATION_ACTIVE, false);
            if (delegateFirst === false) {
              $delegate[operation].apply(self, selfArgs);
            }
          };

          $rootScope.$$postDigest(function() {
            var animationExpression = element.attr('fa-animate-' + operation);

            // If no animation has been specified, delegate the animation event and return
            if (animationExpression === undefined) {
              doneCallback();
              return;
            }

            var animationDuration = $parse(animationExpression)(
              element.scope(),
              {
                $done: doneCallback
              }
            );

            if (typeof animationDuration === 'number') {
              Timer.setTimeout(doneCallback, animationDuration);
            }
          });
        };
      });

      return animationHandlers;
    }]);
  }]);
<commit_msg>Typo fix in docs where 'scope' should be '$scope'<commit_after>/**
 * @ngdoc service
 * @name $animate
 * @function
 *
 * @description
 * The Famo.us/Angular implementation of the `$animate` service provides Famo.us animation support for
 * Angular's core enter, leave, and move structural events.
 *
 * With the attributes `fa-animate-enter`, `fa-animate-leave`, `fa-animate-move`, you can assign an arbitrary
 * expression to animation events.
 *
 * To notify Famo.us/Angular when your animations are complete, you must do one of two things:
 * either pass a `$done` callback in your animation expressions, or design your animation expressions to
 * evaluate as the numeric duration, in milliseconds, of the animation. If an animation expression
 * both evaluates as a non-number and fails to invoke the `$done` callback, the animation event pipeline
 * will not resolve correctly and items will fail to enter, leave, and move correctly.
 *
 * To inform Famo.us/Angular how to halt any in-progress animation, use the `fa-animate-halt` attribute.
 *
 * The core Angular animation API is fundamentally CSS class-based. Because only Famo.us Surfaces
 * support CSS classes, core directives such as `ngClass`, `ngShow`, `ngIf`, and others should be applied
 * only with directives representing Surfaces (such as {@link faSurface faSurface} and
 * {@link faImageSurface faImageSurface}).
 *
 * The {@link https://docs.angularjs.org/api/ngAnimate ngAnimate} module's documentation lists the set of
 * core directives supporting $animate events. Please note that the `ngAnimate` module is *not* required
 * (or recommended) to implement $animate events with Famo.us, but it is compatible and technically effective
 * on Surfaces.
 *
 * @usage
 * ```html
 * <ANY
 *   fa-animate-enter="expression"
 *   fa-animate-leave="expression"
 *   fa-animate-move="expression"
 *   fa-animate-halt="expression"
 *   ...
 * >
 * </ANY>
 * ```
 * @example
 * ```html
 * <fa-modifier
 *   ng-repeat="item in items"
 *   fa-rotate-y="transitionable.get()"
 *   fa-animate-enter="enter()"
 *   fa-animate-leave="leave($done)"
 *   fa-animate-halt="halt()"
 * >
 *   ...
 * </fa-modifier>
 * ```
 * ```javascript
 * var Transitionable = $famous['famous/transitions/Transitionable'];
 * var SnapTransition = $famous['famous/transitions/SnapTransition'];
 * var DURATION = 500;
 *
 * $scope.transitionable = new Transitionable(Math.PI / 4);
 *
 * // Fold items down to the right when they enter.
 * $scope.enter = function() {
 *   $scope.transitionable.set(
 *     0,
 *     {
 *       method: SnapTransition,
 *       duration: DURATION
 *     }
 *   );
 *
 *  // Declare the animation duration by returning it as a number
 *  return DURATION;
 * };
 *
 * // Fold items up to the left when they leave.
 * $scope.leave = function(done) {
 *   $scope.transitionable.set(
 *     Math.PI / 2,
 *     {
 *       method: SnapTransition,
 *       duration: DURATION
 *     },
 *     // Execute the done callback after the transition is fully applied
 *     done
 *   );
 * };
 *
 * $scope.halt = function() {
 *   // Halt any active animations
 *   $scope.transitionable.halt();
 * };
 * ```
 */
angular.module('famous.angular')
  .config(['$provide', function($provide) {
    // Hook into the animation system to emit ng-class syncers to surfaces
    $provide.decorator('$animate', ['$delegate', '$rootScope', '$famous', '$parse',
                            function($delegate,   $rootScope,   $famous,   $parse) {

      var Timer   = $famous['famous/utilities/Timer'];

      var FA_ANIMATION_ACTIVE = '$$faAnimationActive';

      /**
       * Pass through $animate methods that are strictly class based.
       * These will work on Surfaces, and will be ignored elsewhere.
       * ngAnimate has a complex API for determining when an animation should be
       * considered "enabled" which we do not need.
       */
      var animationHandlers = {
        enabled: $delegate.enabledß
      };

      angular.forEach(['addClass', 'removeClass'], function(classManipulator) {
        // Stash the original class manipulator so we can apply it later
        var originalManipulator = angular.element.prototype[classManipulator];

        /**
         * Fork the angular.element.prototype class manipulator to delegate class changes
         * down to any transcluded Surfaces created by Famo.us/Angular.
         * @param  {String} className - the class to be added or removed
         * @return {void}
         */
        angular.element.prototype[classManipulator] = function(className) {
          originalManipulator.apply(this, arguments);

          // If and only if the current element represents a Famo.us Surface,
          // AND the class is not an empty string, pass through
          // the addClass and removeClass methods to the underlying renderNode.
          if ($famous.util.isASurface(this) && typeof className === 'string' && className.trim() !== '') {
            $famous.getIsolate(this.scope()).renderNode[classManipulator](className);
          }
          return this;
        };

        /**
         * Core Angular animation events will add and remove classes (such as ng-hide)
         * to affect the display of elements using $animate.addClass. Because Famo.us
         * Surfaces make use of classes, we should pass all class-based modifications
         * directively to their Surfaces whenever possible.
         */
        animationHandlers[classManipulator] = function(element, className, done) {

          $delegate[classManipulator](element, className, done);
          if($famous.util.isFaElement(element)){
            var isolate = $famous.getIsolate(element.scope());
            if ($famous.util.isASurface(element)) {

              var surface = isolate.renderNode;
              angular.forEach(className.split(' '), function(splitClassName) {
                if(splitClassName === 'ng-hide'){
                  if(classManipulator === 'addClass'){
                    isolate.hide();
                  } else if( classManipulator === 'removeClass' ){
                    isolate.show();
                  }
                }else {

                  surface[classManipulator](splitClassName);
                }
              });
            } else {
              angular.forEach(className.split(' '), function(splitClassName) {
                if(splitClassName === 'ng-hide'){
                  if(classManipulator === 'addClass'){
                    isolate.hide();
                  } else if( classManipulator === 'removeClass' ){
                    isolate.show();
                  }
                }
              });
            }
          }
         };
      });

      // There isn't a good way to delegate down to Surface.setClasses
      // because Angular has already negotiated the list of items to add
      // and items to remove. Manually loop through both lists.
      animationHandlers.setClass = function(element, add, remove, done) {

        $delegate.setClass(element, add, remove, done);

        if ($famous.util.isASurface(element)) {
          var surface = $famous.getIsolate(element.scope()).renderNode;
          angular.forEach(add.split(' '), function(className) {
            surface.addClass(className);
          });

          angular.forEach(remove.split(' '), function(className) {
            surface.removeClass(className);
          });
        }
      };

      /**
       * $animate.enter, $animate.leave, and $animate.move events
       * can trigger Famo.us animations with the `fa-animate-enter`,
       * `fa-animate-move`, and `fa-animate-leave` attributes.
       * If defined, each of these properties should evaluate to expressions
       * that equal the duration of their animations in milliseconds.
       *
       * Explicitly declaring the duration of all animations ensures
       * Famo.us will know how long to wait before considering the animation
       * complete and allow Angular to continue manipulating elements and classes.
       */
      angular.forEach(['enter', 'leave', 'move'], function(operation) {
        animationHandlers[operation] = function(element) {
          var self = this;
          var selfArgs = arguments;
          var delegateFirst = (operation === 'enter');

          if (delegateFirst === true) {
             $delegate[operation].apply(this, arguments);
          }

           // Detect if an animation is currently running
          if (element.data(FA_ANIMATION_ACTIVE) === true) {
            $parse(element.attr('fa-animate-halt'))(element.scope());
          }

          // Indicate an animation is currently running
          element.data(FA_ANIMATION_ACTIVE, true);

          var doneCallback = function() {

            var scopeId = element.scope() && element.scope().$id;

            //hide the element on animate.leave
            if(operation === 'leave' && $famous.util.isFaElement(element)){
              var isolate = $famous.getIsolate(element.scope());
              if(isolate && isolate.id) isolate.hide();
             }
            // Abort if the done callback has already been invoked
            if (element.data(FA_ANIMATION_ACTIVE) === false) {
              return;
            }

            // Indicate an animation is no longer running
            element.data(FA_ANIMATION_ACTIVE, false);
            if (delegateFirst === false) {
              $delegate[operation].apply(self, selfArgs);
            }
          };

          $rootScope.$$postDigest(function() {
            var animationExpression = element.attr('fa-animate-' + operation);

            // If no animation has been specified, delegate the animation event and return
            if (animationExpression === undefined) {
              doneCallback();
              return;
            }

            var animationDuration = $parse(animationExpression)(
              element.scope(),
              {
                $done: doneCallback
              }
            );

            if (typeof animationDuration === 'number') {
              Timer.setTimeout(doneCallback, animationDuration);
            }
          });
        };
      });

      return animationHandlers;
    }]);
  }]);
<|endoftext|>


<commit_before>var mongo = require(__dirname + '/lib/database');
var config = require(__dirname + '/_config_');
var _ = require('lodash');

module.exports = function(req, res) {
	mongo.get().then(function(db) {
		// :TODO: pull and cache legislator twitter details to fill user category
		// :TODO: limit response size & order by time
		var options = {
		    "limit": 200,
		    "sort": "user.followers_count"
		}
		db.collection('tweets').find({}, options).toArray(function(err, docs) {
		//db.collection('tweets').find({"user.followers_count": {$gt: config.tweet_follower_celebrity_count}}).toArray(function(err, docs) {
			console.log(docs);

	        var tweets = _.map(docs, function(tweet){
	        	return {
					tweet: tweet.text,
					handle: tweet.user.screen_name,
					avatar: tweet.user.profile_image_url,
					link: 'https://twitter.com/#!/' + tweet.user.id + '/status/' + tweet._id + '/',
					retweet_link: 'https://twitter.com/intent/retweet?tweet_id=' + tweet._id,
					category: 'politician',
					followers: tweet.user.followers_count
	        	}
	        });
	        console.log(tweets);
		    res.jsonp(tweets);

        });

        /*
		var tweets = [{
			tweet: 'I think we should eat book cases #stopthespies What do you think?',
			handle: 'neutralthoughts',
			avatar: 'https://pbs.twimg.com/profile_images/488579015507050497/QvG1ArTx_400x400.jpeg',
			link: 'https://twitter.com/neutralthoughts/status/499062476063776768',
			category: 'politician',
			followers: 1000
		}];
		*/
	}, function(err) {
		throw err;
	});

};
<commit_msg>sort tweets api desc<commit_after>var mongo = require(__dirname + '/lib/database');
var config = require(__dirname + '/_config_');
var _ = require('lodash');

module.exports = function(req, res) {
	mongo.get().then(function(db) {
		// :TODO: pull and cache legislator twitter details to fill user category
		// :TODO: limit response size & order by time
		var options = {
		    "limit": 200,
		    "sort": [["user.followers_count", "desc"]]
		}
		db.collection('tweets').find({}, options).toArray(function(err, docs) {
		//db.collection('tweets').find({"user.followers_count": {$gt: config.tweet_follower_celebrity_count}}).toArray(function(err, docs) {
			console.log(docs);

	        var tweets = _.map(docs, function(tweet){
	        	return {
					tweet: tweet.text,
					handle: tweet.user.screen_name,
					avatar: tweet.user.profile_image_url,
					link: 'https://twitter.com/#!/' + tweet.user.id + '/status/' + tweet._id + '/',
					retweet_link: 'https://twitter.com/intent/retweet?tweet_id=' + tweet._id,
					category: 'politician',
					followers: tweet.user.followers_count
	        	}
	        });
	        console.log(tweets);
		    res.jsonp(tweets);

        });

        /*
		var tweets = [{
			tweet: 'I think we should eat book cases #stopthespies What do you think?',
			handle: 'neutralthoughts',
			avatar: 'https://pbs.twimg.com/profile_images/488579015507050497/QvG1ArTx_400x400.jpeg',
			link: 'https://twitter.com/neutralthoughts/status/499062476063776768',
			category: 'politician',
			followers: 1000
		}];
		*/
	}, function(err) {
		throw err;
	});

};
<|endoftext|>


<commit_before>import jsonfile from 'jsonfile'
import * as collection from '../utils/constants'
import R from 'ramda'
import assert from 'assert'

const checkForMissingEnrolments = data => data.filter(section => !section.hasOwnProperty('enrolment'))
const checkForEnrolmentsThatAreNotNumbers = data => data.filter(section => !Number.isInteger(section.enrolment))
const checkForOnePUID = data => {
  const arrOfNames = R.uniq(data.map(section => [section.instructorName, section.PUID])).map(arr => arr[0])
  const uniqArrOfNames = R.uniq(R.uniq(data.map(section => [section.instructorName, section.PUID])).map(arr => arr[0]))
  if (arrOfNames.length !== uniqArrOfNames.length) {
    const sortedArrOfNames = arrOfNames.sort()
    return sortedArrOfNames.filter((name, i) => name === sortedArrOfNames[i + 1])
  }
  return false
}
const checkMissingProperties = data => data.filter(section => {
  return R.not(section.hasOwnProperty('PUID') &&
    section.hasOwnProperty('UMI1') &&
    section.hasOwnProperty('UMI1') &&
    section.hasOwnProperty('UMI2') &&
    section.hasOwnProperty('UMI3') &&
    section.hasOwnProperty('UMI4') &&
    section.hasOwnProperty('UMI5') &&
    section.hasOwnProperty('UMI6') &&
    section.hasOwnProperty('course') &&
    section.hasOwnProperty('courseLevel') &&
    section.hasOwnProperty('courseName') &&
    section.hasOwnProperty('dept') &&
    section.hasOwnProperty('enrolment') &&
    section.hasOwnProperty('gender') &&
    section.hasOwnProperty('instructorName') &&
    section.hasOwnProperty('meetsMin') &&
    section.hasOwnProperty('responseRate') &&
    section.hasOwnProperty('section') &&
    section.hasOwnProperty('term') &&
    section.hasOwnProperty('year'))
})

jsonfile.readFile('./output/' + collection.aggregatedData + '.json', (err, data) => {
  assert.equal(null, err)

  const missingEnrolments = checkForMissingEnrolments(data)
  const NaNEnrolments = checkForEnrolmentsThatAreNotNumbers(data)
  const onePUID = checkForOnePUID(data)
  const missingProperties = checkMissingProperties(data)

  if (missingEnrolments.length > 0 || NaNEnrolments.length > 0) console.log('These courses are missing enrolment information: ', missingEnrolments, NaNEnrolments)
  if (onePUID) console.log('The following instructors has multiple PUIDs: ', onePUID)
  if (missingProperties.length > 0) console.log('These courses are missing some properties', missingProperties)
  else console.log('No errors were detected!')
})
<commit_msg>added responseRate validation<commit_after>import jsonfile from 'jsonfile'
import * as collection from '../utils/constants'
import R from 'ramda'
import assert from 'assert'

const checkForMissingEnrolments = data => data.filter(section => !section.hasOwnProperty('enrolment'))
const checkForEnrolmentsThatAreNotNumbers = data => data.filter(section => !Number.isInteger(section.enrolment))
const checkForOnePUID = data => {
  const arrOfNames = R.uniq(data.map(section => [section.instructorName, section.PUID])).map(arr => arr[0])
  const uniqArrOfNames = R.uniq(R.uniq(data.map(section => [section.instructorName, section.PUID])).map(arr => arr[0]))
  if (arrOfNames.length !== uniqArrOfNames.length) {
    const sortedArrOfNames = arrOfNames.sort()
    return sortedArrOfNames.filter((name, i) => name === sortedArrOfNames[i + 1])
  }
  return false
}
const checkMissingProperties = data => data.filter(section => {
  return R.not(section.hasOwnProperty('PUID') &&
    section.hasOwnProperty('UMI1') &&
    section.hasOwnProperty('UMI1') &&
    section.hasOwnProperty('UMI2') &&
    section.hasOwnProperty('UMI3') &&
    section.hasOwnProperty('UMI4') &&
    section.hasOwnProperty('UMI5') &&
    section.hasOwnProperty('UMI6') &&
    section.hasOwnProperty('course') &&
    section.hasOwnProperty('courseLevel') &&
    section.hasOwnProperty('courseName') &&
    section.hasOwnProperty('dept') &&
    section.hasOwnProperty('enrolment') &&
    section.hasOwnProperty('gender') &&
    section.hasOwnProperty('instructorName') &&
    section.hasOwnProperty('meetsMin') &&
    section.hasOwnProperty('responseRate') &&
    section.hasOwnProperty('section') &&
    section.hasOwnProperty('term') &&
    section.hasOwnProperty('year'))
})

const checkResponseRate = data => data.filter(section => section.responseRate <= 0 || section.responseRate > 1)

jsonfile.readFile('./output/' + collection.aggregatedData + '.json', (err, data) => {
  assert.equal(null, err)

  const missingEnrolments = checkForMissingEnrolments(data)
  const NaNEnrolments = checkForEnrolmentsThatAreNotNumbers(data)
  const onePUID = checkForOnePUID(data)
  const missingProperties = checkMissingProperties(data)
  const responseRate = checkResponseRate(data)

  if (missingEnrolments.length > 0 || NaNEnrolments.length > 0) console.log('These courses are missing enrolment information: ', missingEnrolments, NaNEnrolments)
  if (onePUID) console.log('The following instructors has multiple PUIDs: ', onePUID)
  if (missingProperties.length > 0) console.log('These courses are missing some properties', missingProperties)
  if (responseRate.length > 0) console.log('These courses have invalid response rates', responseRate)
  else console.log('No errors were detected!')
})
<|endoftext|>


<commit_before><commit_msg>Update emoji labels<commit_after><|endoftext|>


<commit_before>'use strict';

const hooks = require('./hooks');
var conn_arr = {client:'pg',connection:'postgres://postgres@localhost/calender'}
//var db = knex(conn_arr);

class Service {
  constructor(options) {
    this.options = options || {};
    this.db = require('knex')({client: 'pg',connection:'postgres://postgres@localhost/calender'});

  }

  find(params) {
    return Promise.resolve([]);
  }

  get(id, params) {
    return this.db.raw('SELECT c_id,name,takes_class_recurring.at_time FROM class_recurring INNER JOIN takes_class_recurring using (c_id)\
     INNER JOIN student using (usn)WHERE usn like ? ORDER BY class_recurring.at_time;',[id])
      .then((d)=>d.rows);

  }

  create(data, params) {
    if(Array.isArray(data)) {
      return Promise.all(data.map(current => this.create(current)));
    }

    return Promise.resolve(data);
  }

  update(id, data, params) {
    return Promise.resolve(data);
  }

  patch(id, data, params) {
    return Promise.resolve(data);
  }

  remove(id, params) {
    return Promise.resolve({ id });
  }
}

module.exports = function(){
  const app = this;

  // Initialize our service with any options it requires
  app.use('/student_classes', new Service());

  // Get our initialize service to that we can bind hooks
  const student_classesService = app.service('/student_classes');

  // Set up our before hooks
  student_classesService.before(hooks.before);

  // Set up our after hooks
  student_classesService.after(hooks.after);
};

module.exports.Service = Service;
<commit_msg>change query<commit_after>'use strict';

const hooks = require('./hooks');
var conn_arr = {client:'pg',connection:'postgres://postgres@localhost/calender'}
//var db = knex(conn_arr);

class Service {
  constructor(options) {
    this.options = options || {};
    this.db = require('knex')({client: 'pg',connection:'postgres://postgres@localhost/calender'});

  }

  find(params) {
    return Promise.resolve([]);
  }

  get(id, params) {
    return this.db.raw('SELECT c_id,name,takes_class_recurring.at_time FROM class_recurring INNER JOIN takes_class_recurring using (c_id)\
     INNER JOIN student using (usn)WHERE usn ilike ? ORDER BY class_recurring.at_time;',[id])
      .then((d)=>d.rows);
  }

  create(data, params) {
    if(Array.isArray(data)) {
      return Promise.all(data.map(current => this.create(current)));
    }

    return Promise.resolve(data);
  }

  update(id, data, params) {
    return Promise.resolve(data);
  }

  patch(id, data, params) {
    return Promise.resolve(data);
  }

  remove(id, params) {
    return Promise.resolve({ id });
  }
}

module.exports = function(){
  const app = this;

  // Initialize our service with any options it requires
  app.use('/student_classes', new Service());

  // Get our initialize service to that we can bind hooks
  const student_classesService = app.service('/student_classes');

  // Set up our before hooks
  student_classesService.before(hooks.before);

  // Set up our after hooks
  student_classesService.after(hooks.after);
};

module.exports.Service = Service;
<|endoftext|>


<commit_before>/* global Showdown */

'use strict';

var MsgPopupDirective = function (staticContent, $mdDialog, $translate) {
    return {
        restrict: 'A',
        link: function (scope, elem, attrs) {
            if (localStorage['popups.' + attrs.popupId]) {
                return;
            }
            staticContent.getMarkdown('popups_' + attrs.popupId, function (err, data) {
                if (!err) {
                    scope.popupContent = data;
                    var converter = new Showdown.converter(),
                        html = converter.makeHtml(data);
                    $mdDialog.show($mdDialog.confirm()
                        .clickOutsideToClose(true)
                        .title($translate.instant('popups.' + attrs.popupId + '.title'))
                        .htmlContent(html)
                        .hasBackdrop(false)
                        .ariaLabel($translate.instant('popups.' + attrs.popupId + '.title'))
                        .ok($translate.instant('actions.ok'))
                        .cancel($translate.instant('actions.dont_show_again')))
                        .then(function () {}, function () {
                            try {
                                localStorage['popups.' + attrs.popupId] = '1';
                            } catch (e) {
                                console.log('Warning: LocalStorage is not available.');
                            }
                        });
                }
            });
        }
    };
};

MsgPopupDirective.$inject = ['staticContent', '$mdDialog', '$translate'];

module.exports = MsgPopupDirective;<commit_msg>Follow-up: remove showdown<commit_after>/* global marked */

'use strict';

var MsgPopupDirective = function (staticContent, $mdDialog, $translate) {
    return {
        restrict: 'A',
        link: function (scope, elem, attrs) {
            if (localStorage['popups.' + attrs.popupId]) {
                return;
            }
            staticContent.getMarkdown('popups_' + attrs.popupId, function (err, data) {
                if (!err) {
                    scope.popupContent = data;
                    var html = marked(data);
                    $mdDialog.show($mdDialog.confirm()
                        .clickOutsideToClose(true)
                        .title($translate.instant('popups.' + attrs.popupId + '.title'))
                        .htmlContent(html)
                        .hasBackdrop(false)
                        .ariaLabel($translate.instant('popups.' + attrs.popupId + '.title'))
                        .ok($translate.instant('actions.ok'))
                        .cancel($translate.instant('actions.dont_show_again')))
                        .then(function () {}, function () {
                            try {
                                localStorage['popups.' + attrs.popupId] = '1';
                            } catch (e) {
                                console.log('Warning: LocalStorage is not available.');
                            }
                        });
                }
            });
        }
    };
};

MsgPopupDirective.$inject = ['staticContent', '$mdDialog', '$translate'];

module.exports = MsgPopupDirective;
<|endoftext|>


<commit_before>var Promise = require('bluebird'),
    _ = require('lodash')

var findByTagName = require('../findBy/tagname')
var findByExample = require('../findBy/example')
var findByListingInfo = require('../findBy/listingInfo')

function DB() {}

DB.prototype.init = function() {
    this.env = {}
    var self = this
    return Promise.join(
        findByTagName.init(this.env),
        findByExample.init(this.env),
        findByListingInfo.init(this.env)
    ).then(function() {
        return self
    })
}

function _apply_projection(env, results, projection) {

    // e.g.,
    // projection = {id: true, packageName: true}

    return results.map(function(id) {
        var app = env.apps.get(id, projection)
        return app
    })
}

DB.prototype.find = function(q, options) {

    var options = {
        limit: 5,
        scope: _.range(0, 100)
    }

    var env = this.env

    return findByListingInfo.find(env, q.listing, options)
        .then(function(results) {

            options.scope = results
            return findByTagName.find(env, q.ui, options)
        })
        .then(function(results) {

            options.scope = results
            return findByExample.find(env, q.ui, options)
        })
        .then(function(results) {

            return _apply_projection(env, results, options.projection)
        })
}

module.exports = DB<commit_msg>add the remaining query levels<commit_after>var Promise = require('bluebird'),
    _ = require('lodash'),
    findByTagName = require('../findBy/tagname'),
    findByUI = require('../findBy/ui'),
    findByManifest = require('../findBy/manifest'),
    findByListing = require('../findBy/listing'),
    findByCode = require('../findBy/code'),
    mongoConnection = require('../db/connection')

function DB() {
}

DB.prototype.init = function () {
    this.env = {}
    var self = this
    return Promise.join(
        findByTagName.init(this.env),
        findByUI.init(this.env),
        findByListing.init(this.env),
        findByManifest.init(this.env),
        findByCode.init(this.env),
        mongoConnection
    ).then(function (results) {
            self.mongo = results[5]
            return self
        })
        .error(function (e) {
            console.error('Failed to initialize the database. Reason: ' + e.message)
        })
}

function _apply_projection(env, results, projection) {

    // e.g.,
    // projection = {id: true, packageName: true}

    return results.map(function (id) {
        var app = env.apps.get(id, projection)
        return app
    })
}

DB.prototype.find = function (query) {

    var mongo = this.mongo
    var env = this.env
    var options = {
        limit: query.limit,
        scope: _.range(0, 60)
    }
    return findByListing.find(env, query.listing, mongo, options)
        .then(function (results) {
            options.scope = results
            return findByManifest.find(env, query.manifest, options)
        })
        .then(function (results) {
            options.scope = results
            return findByCode.find(env, query.code, options)
        })
        .then(function (results) {
            options.scope = results
            return findByTagName.find(env, query.ui, options);
        })
        .then(function (results) {
            options.scope = results
            return findByUI.find(env, query.ui, options)
        })
        .then(function (results) {
            return _apply_projection(env, results, options.projection)
        })
        .catch(function (e) {
            console.error(e)
        });
}

module.exports = DB<|endoftext|>


<commit_before>'use strict';

if(!process.env.DEBUG){
  return (module.exports = null);
}

var path = require('path');
var debug = process.env.DEBUG.trim().split(/\#/);

exports = module.exports = {
  path: {},
  skipFile: function flagsSkipFile(filename){
    return !(
      this.star ||
      this.path[filename] ||
      this.path[path.dirname(filename)]
    );
  }
};

debug[0].split(/[, ]+/).map(function(filename){
  if(/\*/.test(filename)){
    if(filename === '*'){ return (exports.star = true); }
    filename = path.resolve(filename.split('*')[0] || '');
  } else if(/[\\//]$/.test(filename)){
    filename = path.resolve(filename, '..');
  } else if(!path.extname(filename)){
    filename = path.resolve(filename + '.js');
  }

  exports.path[filename] = true;
});

if(debug.length < 2){ return ; }

exports.fn = {};

debug.slice(1).forEach(function(name){
  exports.fn[name] = true;
});
<commit_msg>dev: fix resolved paths for dirnames<commit_after>'use strict';

if(!process.env.DEBUG){
  return (module.exports = null);
}

var path = require('path');
var debug = process.env.DEBUG.trim().split(/\#/);

exports = module.exports = {
  path: {},
  skipFile: function flagsSkipFile(filename){
    return !(
      this.star ||
      this.path[filename] ||
      this.path[path.dirname(filename)]
    );
  }
};

debug[0].split(/[, ]+/).map(function(filename){
  if(/\*/.test(filename)){
    if(filename === '*'){ return (exports.star = true); }
    filename = filename.split('*')[0] || '';
  } else if(!/[\\//]$/.test(filename) && !path.extname(filename)){
    filename += '.js';
  }

  exports.path[path.resolve(filename)] = true;
});

if(debug.length < 2){ return ; }

exports.fn = {};

debug.slice(1).forEach(function(name){
  exports.fn[name] = true;
});
<|endoftext|>


<commit_before>var
  dirty = exports,
  File = require('file').File;

dirty.uuid = function() {
  var uuid = '', i;
  for (i = 0; i < 32; i++) {
    uuid += Math.floor(Math.random() * 16).toString(16);
  }
  return uuid;
};

var Dirty = dirty.Dirty = function(file, options) {
  process.EventEmitter.call(this);
  options = process.mixin({
    flushInterval: 10,
    flushLimit: 1000,
  }, options);

  this.file = (file)
    ? new File(file, 'a+', {encoding: 'utf8'})
    : false;

  this._docs = [];
  this._ids = {};
  this._memoryIds = [];

  // Interval after which docs should be flushed to disk at latest
  this.flushInterval = options.flushInterval;

  // Listeners waiting for the next flush to finish
  this.flushCallbacks = [];

  // Wait for x docs to be in memory before flushing to disk
  this.flushLimit = options.flushLimit;

  // Candidates for being flushed to disk
  this.memoryQueueLength = 0;
  // Docs in the process of being flushed to disk
  this.flushQueueLength = 0;

  this.length = 0;
};
process.inherits(Dirty, process.EventEmitter);

Dirty.prototype.load = function() {
  var
    self = this,
    promise = new process.Promise(),
    buffer = '',
    offset = 0,
    read = function() {
      self.file.read(16*1024).addCallback(function(chunk) {
        if (!chunk) {
          return promise.emitSuccess();
        }

        buffer += chunk;
        while ((offset = buffer.indexOf("\n")) !== -1) {
          var doc = JSON.parse(buffer.substr(0, offset));
          if (!(doc._id in self._ids) && !doc._deleted) {
            self.length++;
          }
          if (doc._deleted) {
            if (doc._id in self._ids) {
              self._docs.splice(self._ids[doc._id], 1);
              delete self._ids[doc._id];
            }
          } else {
            self._ids[doc._id] = (self._docs.push(doc)-1);
          }
          buffer = buffer.substr(offset+1);
        }
        read();
      })
      .addErrback(function() {
        promise.emitError(new Error('could not read from '+self.file.filename));
      });
    }

  read();
  return promise;
};

Dirty.prototype.close = function() {
  clearTimeout(this.flushTimer);
  return this.file.close();
};

Dirty.prototype.add = function(doc, cb) {
  var uuid = dirty.uuid();
  this.set(uuid, doc, cb);
  return uuid;
};

Dirty.prototype.set = function(id, doc, cb) {
  doc._id = id;

  if (this._ids[id] === undefined && !doc._deleted) {
    this.length++;
  }
  this._ids[id] = (this._docs.push(doc)-1);
  if (!this.file) {
    process.nextTick(function() {
      cb(doc);
    });
    return;
  }

  this._memoryIds.push(id);
  this.memoryQueueLength++;

  if (this.memoryQueueLength === this.flushLimit) {
    this.flush().addCallback(function() {
      if (cb) {
        cb(doc);
      }
    });
  } else if (cb) {
    this.flushCallbacks.push(function() {
      cb(doc);
    });
  }

  // Start new flushTimer if needed
  if (
    (!this.flushTimer)
    && (this.flushInterval)
    && (this.memoryQueueLength !== this.flushLimit)
  ) {
    var self = this;
    this.flushTimer = setTimeout(function() {
      self.flush();
    }, this.flushInterval);
  }
};

Dirty.prototype.empty = function() {
  this._ids = {};
  this._docs = [];
};

Dirty.prototype.flush = function() {
  var promise = new process.Promise();
  if (this.memoryQueueLength === 0 || !this.file) {
    promise.emitSuccess();
    return promise;
  }

  var
    self = this,
    chunk = '',
    length = this._memoryIds.length,
    writePromises = 0,
    dupes = {};

  this.flushQueueLength += length;

  this._memoryIds.forEach(function(id, i) {
    if (!(id in dupes)) {
      chunk += JSON.stringify(self._docs[self._ids[id]])+"\n";
    }
    dupes[id] = true;

    if (chunk.length < 16*1024 && i < (length-1)) {
      return;
    }

    writePromises++;
    self.file.write(chunk).addCallback(function() {
      writePromises--;
      if (writePromises === 0) {
        self.flushQueueLength -= length;
        self.flushCallbacks.forEach(function(cb) {
          cb();
        });
        self.flushCallbacks = [];
        promise.emitSuccess();
      }

      // If memory === disk, emit the flush event
      if (self.flushQueueLength === 0 && self.memoryQueueLength === 0) {
        // Clear flushTimer so the event loop can exit when idle
        clearTimeout(self.flushTimer);
        self.flushTimer = null;

        self.emit('flush');
      }
    });

    chunk = '';
  });
  this._memoryIds = [];
  this.memoryQueueLength = 0;

  return promise;
};

Dirty.prototype.get = function(id) {
  var doc = this._docs[this._ids[id]];
  return (doc && doc._deleted)
    ? undefined
    : doc;
};

Dirty.prototype.remove = function(id, cb) {
  var self = this;

  delete this._docs[this._ids[id]];

  this.length--;
  this.set(id, {_deleted: true}, function() {
    delete self._docs[self._ids[id]];
    delete(self._ids[id]);

    if (cb) {
      cb();
    }
  });
};

Dirty.prototype.filter = function(fn) {
  return this._docs.filter(fn);
};<commit_msg>Add seek() functionality<commit_after>var
  dirty = exports,
  File = require('file').File;

dirty.uuid = function() {
  var uuid = '', i;
  for (i = 0; i < 32; i++) {
    uuid += Math.floor(Math.random() * 16).toString(16);
  }
  return uuid;
};

var Dirty = dirty.Dirty = function(file, options) {
  process.EventEmitter.call(this);
  options = process.mixin({
    flushInterval: 10,
    flushLimit: 1000,
  }, options);

  this.file = (file)
    ? new File(file, 'a+', {encoding: 'utf8'})
    : false;

  this._docs = [];
  this._ids = {};
  this._memoryIds = [];

  // Interval after which docs should be flushed to disk at latest
  this.flushInterval = options.flushInterval;

  // Listeners waiting for the next flush to finish
  this.flushCallbacks = [];

  // Wait for x docs to be in memory before flushing to disk
  this.flushLimit = options.flushLimit;

  // Candidates for being flushed to disk
  this.memoryQueueLength = 0;
  // Docs in the process of being flushed to disk
  this.flushQueueLength = 0;

  this.length = 0;
};
process.inherits(Dirty, process.EventEmitter);

Dirty.prototype.load = function() {
  var
    self = this,
    promise = new process.Promise(),
    buffer = '',
    offset = 0,
    read = function() {
      self.file.read(16*1024).addCallback(function(chunk) {
        if (!chunk) {
          return promise.emitSuccess();
        }

        buffer += chunk;
        while ((offset = buffer.indexOf("\n")) !== -1) {
          var doc = JSON.parse(buffer.substr(0, offset));
          if (!(doc._id in self._ids) && !doc._deleted) {
            self.length++;
          }
          if (doc._deleted) {
            if (doc._id in self._ids) {
              self._docs.splice(self._ids[doc._id], 1);
              delete self._ids[doc._id];
            }
          } else {
            self._ids[doc._id] = (self._docs.push(doc)-1);
          }
          buffer = buffer.substr(offset+1);
        }
        read();
      })
      .addErrback(function() {
        promise.emitError(new Error('could not read from '+self.file.filename));
      });
    }

  read();
  return promise;
};

Dirty.prototype.close = function() {
  clearTimeout(this.flushTimer);
  return this.file.close();
};

Dirty.prototype.add = function(doc, cb) {
  var uuid = dirty.uuid();
  this.set(uuid, doc, cb);
  return uuid;
};

Dirty.prototype.set = function(id, doc, cb) {
  doc._id = id;

  if (this._ids[id] === undefined && !doc._deleted) {
    this.length++;
  }
  this._ids[id] = (this._docs.push(doc)-1);
  if (!this.file) {
    process.nextTick(function() {
      cb(doc);
    });
    return;
  }

  this._memoryIds.push(id);
  this.memoryQueueLength++;

  if (this.memoryQueueLength === this.flushLimit) {
    this.flush().addCallback(function() {
      if (cb) {
        cb(doc);
      }
    });
  } else if (cb) {
    this.flushCallbacks.push(function() {
      cb(doc);
    });
  }

  // Start new flushTimer if needed
  if (
    (!this.flushTimer)
    && (this.flushInterval)
    && (this.memoryQueueLength !== this.flushLimit)
  ) {
    var self = this;
    this.flushTimer = setTimeout(function() {
      self.flush();
    }, this.flushInterval);
  }
};

Dirty.prototype.empty = function() {
  this._ids = {};
  this._docs = [];
};

Dirty.prototype.flush = function() {
  var promise = new process.Promise();
  if (this.memoryQueueLength === 0 || !this.file) {
    promise.emitSuccess();
    return promise;
  }

  var
    self = this,
    chunk = '',
    length = this._memoryIds.length,
    writePromises = 0,
    dupes = {};

  this.flushQueueLength += length;

  this._memoryIds.forEach(function(id, i) {
    if (!(id in dupes)) {
      chunk += JSON.stringify(self._docs[self._ids[id]])+"\n";
    }
    dupes[id] = true;

    if (chunk.length < 16*1024 && i < (length-1)) {
      return;
    }

    writePromises++;
    self.file.write(chunk).addCallback(function() {
      writePromises--;
      if (writePromises === 0) {
        self.flushQueueLength -= length;
        self.flushCallbacks.forEach(function(cb) {
          cb();
        });
        self.flushCallbacks = [];
        promise.emitSuccess();
      }

      // If memory === disk, emit the flush event
      if (self.flushQueueLength === 0 && self.memoryQueueLength === 0) {
        // Clear flushTimer so the event loop can exit when idle
        clearTimeout(self.flushTimer);
        self.flushTimer = null;

        self.emit('flush');
      }
    });

    chunk = '';
  });
  this._memoryIds = [];
  this.memoryQueueLength = 0;

  return promise;
};

Dirty.prototype.get = function(id) {
  var doc = this._docs[this._ids[id]];
  return (doc && doc._deleted)
    ? undefined
    : doc;
};

Dirty.prototype.remove = function(id, cb) {
  var self = this;

  delete this._docs[this._ids[id]];

  this.length--;
  this.set(id, {_deleted: true}, function() {
    delete self._docs[self._ids[id]];
    delete(self._ids[id]);

    if (cb) {
      cb();
    }
  });
};

Dirty.prototype.filter = function(fn) {
  return this._docs.filter(fn);
};

Dirty.prototype.seek = function(fn, index) {
  this._index = index || this._index || 0;
  for (var i = this._index, l = this._docs.length; i < l; i ++) {
    if (fn(this._docs[i])) return this._docs[i];
  }
  return null;
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) Ajay Sreedhar. All rights reserved.
 *
 * Licensed under the MIT License.
 * Please see LICENSE file located in the project root for more information.
 */

'use strict';

const childProcess = require('node:child_process');
const path = require('node:path');

const grunt = require('grunt');
const rimraf = require('rimraf');

const electron = require('electron');

const {ROOT_DIR} = require('./constant');

function onRendererExit(code) {
    grunt.log.writeln(`Electron exited with code ${code}.`);
}

/* eslint-disable no-console */
function cleanBuild() {
    const done = this.async();

    rimraf(path.join(ROOT_DIR, '{dist,release}'), {disableGlob: false}, (error) => {
        if (error) {
            grunt.log.errorlns([`Could not clean-up: ${error}`]);
            return false;
        }

        grunt.log.oklns(['Cleaned up output directories.']);
        done();

        return true;
    });
}

/* eslint-disable no-console */
function startRenderer() {
    const child = childProcess.spawn(electron, [ROOT_DIR, '--trace-warnings'], {
        stdio: ['pipe', process.stdout, process.stderr]
    });

    child.on('close', onRendererExit);
    child.on('exit', onRendererExit);
    child.on('SIGTERM', onRendererExit);
}

function makeRelease() {
    grunt.log.errorlns(['Unable to make release.']);
}

module.exports = {
    cleanBuild,
    startRenderer,
    makeRelease
};
<commit_msg>fix: Refactor build process<commit_after>/**
 * Copyright (c) Ajay Sreedhar. All rights reserved.
 *
 * Licensed under the MIT License.
 * Please see LICENSE file located in the project root for more information.
 */

'use strict';

const childProcess = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');

const electron = require('electron');
const grunt = require('grunt');
const rimraf = require('rimraf');

const {ROOT_DIR} = require('./constant');
const {buildRelease} = require('./builder-wrapper');

function onRendererExit(code) {
    grunt.log.writeln(`Electron exited with code ${code}.`);
}

/* eslint-disable no-console */
function cleanBuild() {
    const done = this.async();

    rimraf(path.join(ROOT_DIR, '{dist,release}'), {disableGlob: false}, (error) => {
        if (error) {
            grunt.log.errorlns([`Could not clean-up: ${error}`]);
            return false;
        }

        grunt.log.oklns(['Cleaned up output directories.']);
        done();

        return true;
    });
}

/* eslint-disable no-console */
function startRenderer() {
    const child = childProcess.spawn(electron, [ROOT_DIR, '--trace-warnings'], {
        stdio: ['pipe', process.stdout, process.stderr]
    });

    child.on('close', onRendererExit);
    child.on('exit', onRendererExit);
    child.on('SIGTERM', onRendererExit);
}

function makeRelease() {
    if (!fs.existsSync(path.join(ROOT_DIR, 'dist/platform/main.js'))) {
        grunt.fail.fatal('Project not compiled yet! Run `yarn run dist` first.', 0);
        return 0;
    }

    const done = this.async();
    const release = buildRelease();

    release.then((path) => {
        grunt.log.oklns([`Created release: ${path}`]);
    });

    release.catch((error) => {
        grunt.log.errorlns([`Unable to make release: ${error}`]);
    });

    release.finally(done);
}

module.exports = {
    cleanBuild,
    startRenderer,
    makeRelease
};
<|endoftext|>