<commit_before>'use strict'

RoomPosition.prototype.getAdjacent = function () {
  const results = []
  const room = Game.rooms[this.roomName]

  // Row -1
  if (this.y - 1 >= 0) {
    if (this.x - 1 >= 0) {
      results.push(room.getPositionAt(this.x - 1, this.y - 1))
    }
    results.push(room.getPositionAt(this.x, this.y - 1))
    if (this.x + 1 <= 49) {
      results.push(room.getPositionAt(this.x + 1, this.y - 1))
    }
  }

  // Row 0
  if (this.x - 1 >= 0) {
    results.push(room.getPositionAt(this.x - 1, this.y))
  }
  // placeholder for "self"
  if (this.x + 1 <= 49) {
    results.push(room.getPositionAt(this.x + 1, this.y))
  }

  // Row +1
  if (this.y + 1 <= 49) {
    if (this.x - 1 >= 0) {
      results.push(room.getPositionAt(this.x - 1, this.y + 1))
    }
    results.push(room.getPositionAt(this.x, this.y + 1))
    if (this.x + 1 <= 49) {
      results.push(room.getPositionAt(this.x + 1, this.y + 1))
    }
  }

  return results
}

RoomPosition.prototype.getSteppableAdjacent = function (includeCreeps = false, includeStructures = true) {
  return _.filter(this.getAdjacent(), function (pos) {
    return pos.isSteppable(includeCreeps, includeStructures)
  })
}

RoomPosition.prototype.getAdjacentInRange = function (range = 1) {
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  let positions = []
  for (let x = bounds.left; x <= bounds.right; x++) {
    for (let y = bounds.top; y <= bounds.bottom; y++) {
      positions.push(new RoomPosition(x, y, this.roomName))
    }
  }
  return positions
}

RoomPosition.prototype.getSteppableAdjacentInRange = function (range = 1) {
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  let positions = []
  let position
  for (let x = bounds.left; x <= bounds.right; x++) {
    for (let y = bounds.top; y <= bounds.bottom; y++) {
      position = new RoomPosition(x, y, this.roomName)
      if (position.isSteppable()) positions.push(position)
    }
  }
  return positions
}

RoomPosition.prototype.isSteppable = function (includeCreeps = false, includeStructures = true) {
  if (this.getTerrainAt() === 'wall') {
    return false
  }
  if (includeStructures) {
    const structures = this.lookFor(LOOK_STRUCTURES)
    let structure
    for (structure of structures) {
      if (OBSTACLE_OBJECT_TYPES.indexOf(structure.structureType) >= 0) {
        return false
      }
    }
  }
  if (includeCreeps) {
    if (this.lookFor(LOOK_CREEPS).length > 0) {
      return false
    }
  }
  return true
}

RoomPosition.prototype.getLink = function () {
  if (this.__link) {
    return this.__link
  }
  const room = Game.rooms[this.roomName]
  if (!room || !room.structures[STRUCTURE_LINK]) {
    return false
  }
  const pos = this
  const links = _.filter(room.structures[STRUCTURE_LINK], a => pos.getRangeTo(a) <= 2)
  if (links.length < 1) {
    return false
  }
  this.__link = links[0]
  return this.__link
}

RoomPosition.prototype.getActiveLink = function () {
  const link = this.getLink()
  if (!link) {
    return false
  }

  if (this.__linkActive) {
    return link
  }

  if (typeof this.__linkActive === 'undefined') {
    this.__linkActive = link.isActive()
  }

  return (this.__linkActive) ? link : false
}

RoomPosition.prototype.getMostOpenNeighbor = function (isBuildable = false, includeStructures = true) {
  const steppable = this.getSteppableAdjacent()
  let pos
  let best
  let score = 0
  for (pos of steppable) {
    if (isBuildable) {
      if (pos.inFrontOfExit() || pos.isEdge()) {
        continue
      }
    }
    const posScore = pos.getSteppableAdjacent(false, includeStructures).length
    if (posScore > score) {
      score = posScore
      best = pos
    }
  }
  return best
}

RoomPosition.prototype.isEdge = function () {
  return this.x === 49 || this.x === 0 || this.y === 49 || this.y === 0
}

RoomPosition.prototype.isExit = function () {
  return this.isEdge() && Game.map.getTerrainAt(this) !== 'wall'
}

RoomPosition.prototype.inFrontOfExit = function () {
  if (this.isEdge()) {
    return false
  }
  const neighbors = this.getAdjacent()
  let neighbor
  for (neighbor of neighbors) {
    if (neighbor.isExit()) {
      return true
    }
  }
  return false
}

RoomPosition.prototype.getTerrainAt = function () {
  return Game.map.getTerrainAt(this)
}

RoomPosition.prototype.getManhattanDistance = function (pos) {
  return Math.abs(this.x - pos.x) + Math.abs(this.y - pos.y)
}

RoomPosition.prototype.serialize = function () {
  return Room.serializeName(this.roomName) + String.fromCharCode(((this.x * 100) + +this.y) + 200)
}

RoomPosition.deserialize = function (string) {
  const roomname = Room.deserializeName(string.slice(0, -1))
  const coordraw = (string.charCodeAt(string.length - 1) - 200)
  const x = Math.floor(coordraw / 100)
  const y = coordraw % 50
  return new RoomPosition(x, y, roomname)
}

RoomPosition.prototype.lookAroundFor = function (type, range = 1) {
  if (!Game.rooms[this.roomName]) {
    return ERR_INVALID_TARGET
  }
  const room = Game.rooms[this.roomName]
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  return room.lookForAtArea(type, bounds.top, bounds.left, bounds.bottom, bounds.right, true)
}

RoomPosition.prototype.lookAround = function (range = 1) {
  if (!Game.rooms[this.roomName]) {
    return ERR_INVALID_TARGET
  }
  const room = Game.rooms[this.roomName]
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  return room.lookAtArea(bounds.top, bounds.left, bounds.bottom, bounds.right, true)
}

RoomPosition.prototype.getStructureByType = function (structureType) {
  let structures = this.lookFor(LOOK_STRUCTURES)
  let filteredStructures = _.filter(structures, function (structure) {
    return structure.structureType === structureType
  })
  return filteredStructures.length > 0 ? filteredStructures[0] : false
}

RoomPosition.prototype.getBoundingBoxForRange = function (range) {
  return createBoundingBoxForRange(this.x, this.y, range)
}

/**
 * Creates a bounding box clamped inside the borders of the room
 * @param {number} x position to get bounds-range from
 * @param {number} y position to get bounds-range from
 * @param {number} range of the bounding box
 * @returns {{left: number, right: number, top: number, bottom: number}}
 */
function createBoundingBoxForRange (x, y, range) {
  const absRange = Math.abs(range)
  const left = Math.min(Math.max(x - absRange, 0), 49)
  const right = Math.max(Math.min(x + absRange, 49), 0)
  const top = Math.min(Math.max(y - absRange, 0), 49)
  const bottom = Math.max(Math.min(y + absRange, 49), 0)
  return {left, right, top, bottom}
}

module.exports = {
  createBoundingBoxForRange
}
<commit_msg>link.isActive check only for rooms under rcl 8<commit_after>'use strict'

RoomPosition.prototype.getAdjacent = function () {
  const results = []
  const room = Game.rooms[this.roomName]

  // Row -1
  if (this.y - 1 >= 0) {
    if (this.x - 1 >= 0) {
      results.push(room.getPositionAt(this.x - 1, this.y - 1))
    }
    results.push(room.getPositionAt(this.x, this.y - 1))
    if (this.x + 1 <= 49) {
      results.push(room.getPositionAt(this.x + 1, this.y - 1))
    }
  }

  // Row 0
  if (this.x - 1 >= 0) {
    results.push(room.getPositionAt(this.x - 1, this.y))
  }
  // placeholder for "self"
  if (this.x + 1 <= 49) {
    results.push(room.getPositionAt(this.x + 1, this.y))
  }

  // Row +1
  if (this.y + 1 <= 49) {
    if (this.x - 1 >= 0) {
      results.push(room.getPositionAt(this.x - 1, this.y + 1))
    }
    results.push(room.getPositionAt(this.x, this.y + 1))
    if (this.x + 1 <= 49) {
      results.push(room.getPositionAt(this.x + 1, this.y + 1))
    }
  }

  return results
}

RoomPosition.prototype.getSteppableAdjacent = function (includeCreeps = false, includeStructures = true) {
  return _.filter(this.getAdjacent(), function (pos) {
    return pos.isSteppable(includeCreeps, includeStructures)
  })
}

RoomPosition.prototype.getAdjacentInRange = function (range = 1) {
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  let positions = []
  for (let x = bounds.left; x <= bounds.right; x++) {
    for (let y = bounds.top; y <= bounds.bottom; y++) {
      positions.push(new RoomPosition(x, y, this.roomName))
    }
  }
  return positions
}

RoomPosition.prototype.getSteppableAdjacentInRange = function (range = 1) {
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  let positions = []
  let position
  for (let x = bounds.left; x <= bounds.right; x++) {
    for (let y = bounds.top; y <= bounds.bottom; y++) {
      position = new RoomPosition(x, y, this.roomName)
      if (position.isSteppable()) positions.push(position)
    }
  }
  return positions
}

RoomPosition.prototype.isSteppable = function (includeCreeps = false, includeStructures = true) {
  if (this.getTerrainAt() === 'wall') {
    return false
  }
  if (includeStructures) {
    const structures = this.lookFor(LOOK_STRUCTURES)
    let structure
    for (structure of structures) {
      if (OBSTACLE_OBJECT_TYPES.indexOf(structure.structureType) >= 0) {
        return false
      }
    }
  }
  if (includeCreeps) {
    if (this.lookFor(LOOK_CREEPS).length > 0) {
      return false
    }
  }
  return true
}

RoomPosition.prototype.getLink = function () {
  if (this.__link) {
    return this.__link
  }
  const room = Game.rooms[this.roomName]
  if (!room || !room.structures[STRUCTURE_LINK]) {
    return false
  }
  const pos = this
  const links = _.filter(room.structures[STRUCTURE_LINK], a => pos.getRangeTo(a) <= 2)
  if (links.length < 1) {
    return false
  }
  this.__link = links[0]
  return this.__link
}

RoomPosition.prototype.getActiveLink = function () {
  const link = this.getLink()
  if (!link) {
    return false
  }

  if (this.__linkActive) {
    return link
  }

  if (typeof this.__linkActive === 'undefined') {
    this.__linkActive = Game.rooms[this.roomName].controller.level === 8 || link.isActive()
  }

  return (this.__linkActive) ? link : false
}

RoomPosition.prototype.getMostOpenNeighbor = function (isBuildable = false, includeStructures = true) {
  const steppable = this.getSteppableAdjacent()
  let pos
  let best
  let score = 0
  for (pos of steppable) {
    if (isBuildable) {
      if (pos.inFrontOfExit() || pos.isEdge()) {
        continue
      }
    }
    const posScore = pos.getSteppableAdjacent(false, includeStructures).length
    if (posScore > score) {
      score = posScore
      best = pos
    }
  }
  return best
}

RoomPosition.prototype.isEdge = function () {
  return this.x === 49 || this.x === 0 || this.y === 49 || this.y === 0
}

RoomPosition.prototype.isExit = function () {
  return this.isEdge() && Game.map.getTerrainAt(this) !== 'wall'
}

RoomPosition.prototype.inFrontOfExit = function () {
  if (this.isEdge()) {
    return false
  }
  const neighbors = this.getAdjacent()
  let neighbor
  for (neighbor of neighbors) {
    if (neighbor.isExit()) {
      return true
    }
  }
  return false
}

RoomPosition.prototype.getTerrainAt = function () {
  return Game.map.getTerrainAt(this)
}

RoomPosition.prototype.getManhattanDistance = function (pos) {
  return Math.abs(this.x - pos.x) + Math.abs(this.y - pos.y)
}

RoomPosition.prototype.serialize = function () {
  return Room.serializeName(this.roomName) + String.fromCharCode(((this.x * 100) + +this.y) + 200)
}

RoomPosition.deserialize = function (string) {
  const roomname = Room.deserializeName(string.slice(0, -1))
  const coordraw = (string.charCodeAt(string.length - 1) - 200)
  const x = Math.floor(coordraw / 100)
  const y = coordraw % 50
  return new RoomPosition(x, y, roomname)
}

RoomPosition.prototype.lookAroundFor = function (type, range = 1) {
  if (!Game.rooms[this.roomName]) {
    return ERR_INVALID_TARGET
  }
  const room = Game.rooms[this.roomName]
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  return room.lookForAtArea(type, bounds.top, bounds.left, bounds.bottom, bounds.right, true)
}

RoomPosition.prototype.lookAround = function (range = 1) {
  if (!Game.rooms[this.roomName]) {
    return ERR_INVALID_TARGET
  }
  const room = Game.rooms[this.roomName]
  const bounds = createBoundingBoxForRange(this.x, this.y, range)
  return room.lookAtArea(bounds.top, bounds.left, bounds.bottom, bounds.right, true)
}

RoomPosition.prototype.getStructureByType = function (structureType) {
  let structures = this.lookFor(LOOK_STRUCTURES)
  let filteredStructures = _.filter(structures, function (structure) {
    return structure.structureType === structureType
  })
  return filteredStructures.length > 0 ? filteredStructures[0] : false
}

RoomPosition.prototype.getBoundingBoxForRange = function (range) {
  return createBoundingBoxForRange(this.x, this.y, range)
}

/**
 * Creates a bounding box clamped inside the borders of the room
 * @param {number} x position to get bounds-range from
 * @param {number} y position to get bounds-range from
 * @param {number} range of the bounding box
 * @returns {{left: number, right: number, top: number, bottom: number}}
 */
function createBoundingBoxForRange (x, y, range) {
  const absRange = Math.abs(range)
  const left = Math.min(Math.max(x - absRange, 0), 49)
  const right = Math.max(Math.min(x + absRange, 49), 0)
  const top = Math.min(Math.max(y - absRange, 0), 49)
  const bottom = Math.max(Math.min(y + absRange, 49), 0)
  return {left, right, top, bottom}
}

module.exports = {
  createBoundingBoxForRange
}
<|endoftext|>


<commit_before>(function() {

Facet.Marks.globe_2d = function(opts)
{
    opts = _.defaults(opts || {}, {
        zoom: 3,
        resolution_bias: -1,
        patch_size: 10,
        cache_size: 3, // 3: 64 images; 4: 256 images.
        tile_pattern: function(zoom, x, y) {
            return "http://tile.openstreetmap.org/"+zoom+"/"+x+"/"+y+".png";
        },
        post_process: function(c) { return c; }
    });
    if (opts.interactor) {
        opts.center = opts.interactor.center;
        opts.zoom   = opts.interactor.zoom;
        opts.camera = opts.interactor.camera;
    }

    var patch = Facet.model({
        type: "triangles",
        uv: [[0,0,1,0,1,1,0,0,1,1,0,1], 2],
        vertex: function(min, max) {
            return this.uv.mul(max.sub(min)).add(min);
        }
    });
    var cache_size = 1 << (2 * opts.cache_size);
    var tile_size = 256;
    var tiles_per_line  = 1 << (~~Math.round(Math.log(Math.sqrt(cache_size))/Math.log(2)));
    var super_tile_size = tile_size * tiles_per_line;

    var ctx = Facet._globals.ctx;
    var texture = Facet.texture({
        width: super_tile_size,
        height: super_tile_size
    });

    function new_tile(i) {
        var x = i % tiles_per_line;
        var y = ~~(i / tiles_per_line);
        return {
            texture: texture,
            offset_x: x,
            offset_y: y,
            // 0: inactive,
            // 1: mid-request,
            // 2: ready to draw.
            active: 0,
            x: -1,
            y: -1,
            zoom: -1,
            last_touched: 0
        };
    };

    var tiles = [];
    for (var i=0; i<cache_size; ++i) {
        tiles.push(new_tile(i));
    };

    var min_x = Shade.parameter("float");
    var max_x = Shade.parameter("float");
    var min_y = Shade.parameter("float");
    var max_y = Shade.parameter("float");
    var offset_x = Shade.parameter("float");
    var offset_y = Shade.parameter("float");
    var texture_scale = 1.0 / tiles_per_line;
    var sampler = Shade.parameter("sampler2D");

    var v = patch.vertex(Shade.vec(min_x, min_y), Shade.vec(max_x, max_y));

    var xformed_patch = patch.uv
    // These two lines work around the texture seams on the texture atlas
        .mul((tile_size-1.0)/tile_size)
        .add(0.5/tile_size)
    //
        .add(Shade.vec(offset_x, offset_y))
        .mul(texture_scale)
    ;

    var tile_batch = Facet.bake(patch, {
        gl_Position: opts.camera(v),
        gl_FragColor: opts.post_process(Shade.texture2D(sampler, xformed_patch)),
        mode: Facet.DrawingMode.pass
    });

    if (facet_typeOf(opts.zoom) === "number") {
        opts.zoom = Shade.parameter("float", opts.zoom);
    } else if (Facet.is_shade_expression(opts.zoom) !== "parameter") {
        throw "zoom must be either a number or a parameter";
    }

    var result = {
        tiles: tiles,
        queue: [],
        current_osm_zoom: opts.zoom.get(),
        lat_lon_position: function(lat, lon) {
            return Shade.Scale.Geo.latlong_to_mercator(lat, lon);
        },
        resolution_bias: opts.resolution_bias,
        new_center: function(center_x, center_y, center_zoom) {
            var w = ctx.viewportWidth;
            var zoom_divider = 63.6396;
            var base_zoom = Math.log(w / zoom_divider) / Math.log(2);

            var zoom = this.resolution_bias + base_zoom + (Math.log(center_zoom) / Math.log(2));
            zoom = ~~zoom;
            this.current_osm_zoom = zoom;
            var y = (center_y / (Math.PI * 2) + 0.5) * (1 << zoom);
            var x = (center_x / (Math.PI * 2) + 0.5) * (1 << zoom);
            // var y = (center_lat + 90) / 180 * (1 << zoom);
            // var x = center_lon / 360 * (1 << zoom);
            y = (1 << zoom) - y - 1;
            // x = (x + (1 << (zoom - 1))) & ((1 << zoom) - 1);

            for (var i=-2; i<=2; ++i) {
                for (var j=-2; j<=2; ++j) {
                    var rx = ~~x + i;
                    var ry = ~~y + j;
                    if (ry < 0 || ry >= (1 << zoom))
                        continue;
                    if (rx < 0)
                        continue;
                    if (rx >= (1 << zoom))
                        continue;
                    this.request(rx, ry, ~~zoom);
                }
            }
        },
        get_available_id: function(x, y, zoom) {
            // easy cases first: return available tile or a cache hit
            var now = new Date().getTime();
            for (var i=0; i<cache_size; ++i) {
                if (this.tiles[i].x == x &&
                    this.tiles[i].y == y &&
                    this.tiles[i].zoom == zoom &&
                    this.tiles[i].active != 0) {
                    this.tiles[i].last_touched = now;
                    return i;
                }
            }
            for (i=0; i<cache_size; ++i) {
                if (!this.tiles[i].active) {
                    this.tiles[i].last_touched = now;
                    return i;
                }
            }
            // now we need to bump someone out. who?
            var worst_index = -1;
            var worst_time = 1e30;
            for (i=0; i<cache_size; ++i) {
                if (this.tiles[i].active == 1)
                    // don't use this one, it's getting bumped out
                    continue;
                var score = this.tiles[i].last_touched;
                if (score < worst_time) {
                    worst_time = score;
                    worst_index = i;
                }
            }
            return worst_index;
        },
        init: function() {
            for (var z=0; z<3; ++z)
                for (var i=0; i<(1 << z); ++i)
                    for (var j=0; j<(1 << z); ++j)
                        this.request(i, j, z);
        },
        sanity_check: function() {
            var d = {};
            for (var i=0; i<cache_size; ++i) {
                $("#x" + i).text(this.tiles[i].x);
                $("#y" + i).text(this.tiles[i].y);
                $("#z" + i).text(this.tiles[i].zoom);
                if (this.tiles[i].active !== 2)
                    continue;
                var k = this.tiles[i].x + "-" +
                    this.tiles[i].y + "-" +
                    this.tiles[i].zoom;
                if (d[k] !== undefined) {
                    console.log("BAD STATE!",
                                this.tiles[i].x, this.tiles[i].y, this.tiles[i].zoom,
                                this.tiles[i].active,
                                k);
                    throw "die";
                }
                d[k] = true;
            }
        },
        request: function(x, y, zoom) {
            var that = this;
            var id = this.get_available_id(x, y, zoom);
            if (id === -1) {
                console.log("Could not fulfill request " + x + " " + y + " " + zoom);
                return;
            }
            if (this.tiles[id].x == x &&
                this.tiles[id].y == y &&
                this.tiles[id].zoom == zoom) {
                return;
            }

            that.tiles[id].x = x;
            that.tiles[id].y = y;
            that.tiles[id].zoom = zoom;
            this.tiles[id].active = 1;
            var f = function(x, y, zoom, id) {
                return function() {
                    that.tiles[id].active = 2;
                    that.tiles[id].last_touched = new Date().getTime();
                    // uncomment this during debugging
                    // that.sanity_check();
                    Facet.Scene.invalidate();
                };
            };
            Facet.load_image_into_texture({
                texture: tiles[id].texture,
                src: opts.tile_pattern(zoom, x, y),
                crossOrigin: "anonymous",
                x_offset: tiles[id].offset_x * tile_size,
                y_offset: tiles[id].offset_y * tile_size,
                onload: f(x, y, zoom, id)
            });
        },
        draw: function() {
            this.new_center(opts.center.get()[0],
                            opts.center.get()[1],
                            opts.zoom.get());
            var lst = _.range(cache_size);
            var that = this;
            lst.sort(function(id1, id2) {
                var g1 = Math.abs(tiles[id1].zoom - that.current_osm_zoom);
                var g2 = Math.abs(tiles[id2].zoom - that.current_osm_zoom);
                return g2 - g1;
            });

            sampler.set(texture);
            for (var i=0; i<cache_size; ++i) {
                var t = tiles[lst[i]];
                if (t.active !== 2)
                    continue;
                min_x.set((t.x / (1 << t.zoom))           * Math.PI*2 - Math.PI);
                min_y.set((1 - (t.y + 1) / (1 << t.zoom)) * Math.PI*2 - Math.PI);
                max_x.set(((t.x + 1) / (1 << t.zoom))     * Math.PI*2 - Math.PI);
                max_y.set((1 - t.y / (1 << t.zoom))       * Math.PI*2 - Math.PI);
                offset_x.set(t.offset_x);
                offset_y.set(t.offset_y);
                tile_batch.draw();
            }
        }
    };
    result.init();

    return result;
};

})();
<commit_msg>new debug option adds tile x/y/z markers to each image<commit_after>(function() {

Facet.Marks.globe_2d = function(opts)
{
    opts = _.defaults(opts || {}, {
        zoom: 3,
        resolution_bias: -1,
        patch_size: 10,
        cache_size: 3, // 3: 64 images; 4: 256 images.
        tile_pattern: function(zoom, x, y) {
            return "http://tile.openstreetmap.org/"+zoom+"/"+x+"/"+y+".png";
        },
        debug: false,
        post_process: function(c) { return c; }
    });
    if (opts.interactor) {
        opts.center = opts.interactor.center;
        opts.zoom   = opts.interactor.zoom;
        opts.camera = opts.interactor.camera;
    }

    var patch = Facet.model({
        type: "triangles",
        uv: [[0,0,1,0,1,1,0,0,1,1,0,1], 2],
        vertex: function(min, max) {
            return this.uv.mul(max.sub(min)).add(min);
        }
    });
    var cache_size = 1 << (2 * opts.cache_size);
    var tile_size = 256;
    var tiles_per_line  = 1 << (~~Math.round(Math.log(Math.sqrt(cache_size))/Math.log(2)));
    var super_tile_size = tile_size * tiles_per_line;

    var ctx = Facet._globals.ctx;
    var texture = Facet.texture({
        width: super_tile_size,
        height: super_tile_size
    });

    function new_tile(i) {
        var x = i % tiles_per_line;
        var y = ~~(i / tiles_per_line);
        return {
            texture: texture,
            offset_x: x,
            offset_y: y,
            // 0: inactive,
            // 1: mid-request,
            // 2: ready to draw.
            active: 0,
            x: -1,
            y: -1,
            zoom: -1,
            last_touched: 0
        };
    };

    var tiles = [];
    for (var i=0; i<cache_size; ++i) {
        tiles.push(new_tile(i));
    };

    var min_x = Shade.parameter("float");
    var max_x = Shade.parameter("float");
    var min_y = Shade.parameter("float");
    var max_y = Shade.parameter("float");
    var offset_x = Shade.parameter("float");
    var offset_y = Shade.parameter("float");
    var texture_scale = 1.0 / tiles_per_line;
    var sampler = Shade.parameter("sampler2D");

    var v = patch.vertex(Shade.vec(min_x, min_y), Shade.vec(max_x, max_y));

    var xformed_patch = patch.uv
    // These two lines work around the texture seams on the texture atlas
        .mul((tile_size-1.0)/tile_size)
        .add(0.5/tile_size)
    //
        .add(Shade.vec(offset_x, offset_y))
        .mul(texture_scale)
    ;

    var tile_batch = Facet.bake(patch, {
        gl_Position: opts.camera(v),
        gl_FragColor: opts.post_process(Shade.texture2D(sampler, xformed_patch)),
        mode: Facet.DrawingMode.pass
    });

    if (facet_typeOf(opts.zoom) === "number") {
        opts.zoom = Shade.parameter("float", opts.zoom);
    } else if (Facet.is_shade_expression(opts.zoom) !== "parameter") {
        throw "zoom must be either a number or a parameter";
    }

    var result = {
        tiles: tiles,
        queue: [],
        current_osm_zoom: opts.zoom.get(),
        lat_lon_position: function(lat, lon) {
            return Shade.Scale.Geo.latlong_to_mercator(lat, lon);
        },
        resolution_bias: opts.resolution_bias,
        new_center: function(center_x, center_y, center_zoom) {
            var w = ctx.viewportWidth;
            var zoom_divider = 63.6396;
            var base_zoom = Math.log(w / zoom_divider) / Math.log(2);

            var zoom = this.resolution_bias + base_zoom + (Math.log(center_zoom) / Math.log(2));
            zoom = ~~zoom;
            this.current_osm_zoom = zoom;
            var y = (center_y / (Math.PI * 2) + 0.5) * (1 << zoom);
            var x = (center_x / (Math.PI * 2) + 0.5) * (1 << zoom);
            // var y = (center_lat + 90) / 180 * (1 << zoom);
            // var x = center_lon / 360 * (1 << zoom);
            y = (1 << zoom) - y - 1;
            // x = (x + (1 << (zoom - 1))) & ((1 << zoom) - 1);

            for (var i=-2; i<=2; ++i) {
                for (var j=-2; j<=2; ++j) {
                    var rx = ~~x + i;
                    var ry = ~~y + j;
                    if (ry < 0 || ry >= (1 << zoom))
                        continue;
                    if (rx < 0)
                        continue;
                    if (rx >= (1 << zoom))
                        continue;
                    this.request(rx, ry, ~~zoom);
                }
            }
        },
        get_available_id: function(x, y, zoom) {
            // easy cases first: return available tile or a cache hit
            var now = new Date().getTime();
            for (var i=0; i<cache_size; ++i) {
                if (this.tiles[i].x == x &&
                    this.tiles[i].y == y &&
                    this.tiles[i].zoom == zoom &&
                    this.tiles[i].active != 0) {
                    this.tiles[i].last_touched = now;
                    return i;
                }
            }
            for (i=0; i<cache_size; ++i) {
                if (!this.tiles[i].active) {
                    this.tiles[i].last_touched = now;
                    return i;
                }
            }
            // now we need to bump someone out. who?
            var worst_index = -1;
            var worst_time = 1e30;
            for (i=0; i<cache_size; ++i) {
                if (this.tiles[i].active == 1)
                    // don't use this one, it's getting bumped out
                    continue;
                var score = this.tiles[i].last_touched;
                if (score < worst_time) {
                    worst_time = score;
                    worst_index = i;
                }
            }
            return worst_index;
        },
        init: function() {
            for (var z=0; z<3; ++z)
                for (var i=0; i<(1 << z); ++i)
                    for (var j=0; j<(1 << z); ++j)
                        this.request(i, j, z);
        },
        sanity_check: function() {
            var d = {};
            for (var i=0; i<cache_size; ++i) {
                $("#x" + i).text(this.tiles[i].x);
                $("#y" + i).text(this.tiles[i].y);
                $("#z" + i).text(this.tiles[i].zoom);
                if (this.tiles[i].active !== 2)
                    continue;
                var k = this.tiles[i].x + "-" +
                    this.tiles[i].y + "-" +
                    this.tiles[i].zoom;
                if (d[k] !== undefined) {
                    console.log("BAD STATE!",
                                this.tiles[i].x, this.tiles[i].y, this.tiles[i].zoom,
                                this.tiles[i].active,
                                k);
                    throw "die";
                }
                d[k] = true;
            }
        },
        request: function(x, y, zoom) {
            var that = this;
            var id = this.get_available_id(x, y, zoom);
            if (id === -1) {
                console.log("Could not fulfill request " + x + " " + y + " " + zoom);
                return;
            }
            if (this.tiles[id].x == x &&
                this.tiles[id].y == y &&
                this.tiles[id].zoom == zoom) {
                return;
            }

            that.tiles[id].x = x;
            that.tiles[id].y = y;
            that.tiles[id].zoom = zoom;
            this.tiles[id].active = 1;
            var f = function(x, y, zoom, id) {
                return function() {
                    that.tiles[id].active = 2;
                    that.tiles[id].last_touched = new Date().getTime();
                    // uncomment this during debugging
                    // that.sanity_check();
                    Facet.Scene.invalidate();
                };
            };
            var xform = opts.debug ? function(image) {
                var c = document.createElement("canvas");
                c.setAttribute("width", image.width);
                c.setAttribute("height", image.width);
                var ctx = c.getContext('2d');
                ctx.drawImage(image, 0, 0);
                ctx.font = "12pt Helvetica Neue";
                ctx.fillStyle = "black";
                ctx.fillText(x + " " + y + " " + zoom + " ", 10, 250);
                ctx.lineWidth = 3;
                ctx.strokeStyle = "black";
                ctx.strokeRect(0, 0, 256, 256);
                return c;
            } : function(image) { return image; };
            Facet.load_image_into_texture({
                texture: tiles[id].texture,
                src: opts.tile_pattern(zoom, x, y),
                transform_image: xform,
                crossOrigin: "anonymous",
                x_offset: tiles[id].offset_x * tile_size,
                y_offset: tiles[id].offset_y * tile_size,
                onload: f(x, y, zoom, id)
            });
        },
        draw: function() {
            this.new_center(opts.center.get()[0],
                            opts.center.get()[1],
                            opts.zoom.get());
            var lst = _.range(cache_size);
            var that = this;
            lst.sort(function(id1, id2) {
                var g1 = Math.abs(tiles[id1].zoom - that.current_osm_zoom);
                var g2 = Math.abs(tiles[id2].zoom - that.current_osm_zoom);
                return g2 - g1;
            });

            sampler.set(texture);
            for (var i=0; i<cache_size; ++i) {
                var t = tiles[lst[i]];
                if (t.active !== 2)
                    continue;
                min_x.set((t.x / (1 << t.zoom))           * Math.PI*2 - Math.PI);
                min_y.set((1 - (t.y + 1) / (1 << t.zoom)) * Math.PI*2 - Math.PI);
                max_x.set(((t.x + 1) / (1 << t.zoom))     * Math.PI*2 - Math.PI);
                max_y.set((1 - t.y / (1 << t.zoom))       * Math.PI*2 - Math.PI);
                offset_x.set(t.offset_x);
                offset_y.set(t.offset_y);
                tile_batch.draw();
            }
        }
    };
    result.init();

    return result;
};

})();
<|endoftext|>


<commit_before>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**

*/
foam.CLASS({
  package: 'foam.core',
  name: 'InnerClass',

  // documentation: 'Inner-Class Axiom',

  properties: [
    {
      name: 'name',
      getter: function() { return this.model.name; }
    },
    {
      name: 'model',
      adapt: function(_, m) {
        return foam.core.Model.isInstance(m) ? m : foam.core.Model.create(m);
      }
    }
  ],

  methods: [
    function installInClass(cls) {
      cls[this.model.name] = this.model.buildClass();
    },
    function installInProto(proto) {
      // get class already created in installInClass();
      var name = this.model.name;
      var cls = proto.cls_[name];

      // Create a private_ clone of the Class with the create() method decorated
      // to pass 'this' as the context if not explicitly provided.  This ensures
      // that the created object has access to this object's exported bindings.
      Object.defineProperty(proto, name, {
        get: function innerClassGetter() {
          if ( ! this.hasOwnPrivate_(name) ) {
            var parent = this;
            var c      = Object.create(cls);

            c.create = function innerClassCreate(args, ctx) {
              return cls.create(args, ctx || parent);
            };
            this.setPrivate_(name, c);
          }

          return this.getPrivate_(name);
        },
        configurable: true,
        enumerable: false
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',
  properties: [
    {
      class: 'AxiomArray',
      of: 'InnerClass',
      name: 'classes',
      // A custom adaptArrayElement is needed because we're
      // passing the model definition as model:, rather than
      // as all of the arguments to create().
      adaptArrayElement: function(o) {
        return foam.core.InnerClass.isInstance(o) ?
          o :
          foam.core.InnerClass.create({model: o}) ;
      }
    }
  ]
});
<commit_msg>Added documentation for InnerClass.<commit_after>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
  Classes can have "inner-classes" which are classes which are defined within
  the scope of a class itself rather than being top-level classes which reside
  in a package or globally. This helps to avoid polluting namespaces with classes
  which are only used by a single class.

  Ex.
  // Classes can have inner-Classes.
  foam.CLASS({
    name: 'InnerClassTest',
    classes: [
      { name: 'InnerClass1', properties: ['a', 'b'] },
      { name: 'InnerClass2', properties: ['x', 'y'] }
    ],
    methods: [
      function init() {
        var ic1 = this.InnerClass1.create({a:1, b:2});
        var ic2 = this.InnerClass2.create({x:5, y:10});
        log(ic1.a, ic1.b, ic2.x, ic2.y);
      }
    ]
  });
  InnerClassTest.create();

*/
foam.CLASS({
  package: 'foam.core',
  name: 'InnerClass',

  documentation: 'Inner-Class Axiom',

  properties: [
    {
      name: 'name',
      getter: function() { return this.model.name; }
    },
    {
      name: 'model',
      adapt: function(_, m) {
        return foam.core.Model.isInstance(m) ? m : foam.core.Model.create(m);
      }
    }
  ],

  methods: [
    function installInClass(cls) {
      cls[this.model.name] = this.model.buildClass();
    },

    function installInProto(proto) {
      // get class already created in installInClass();
      var name = this.model.name;
      var cls = proto.cls_[name];

      // Create a private_ clone of the Class with the create() method decorated
      // to pass 'this' as the context if not explicitly provided.  This ensures
      // that the created object has access to this object's exported bindings.
      Object.defineProperty(proto, name, {
        get: function innerClassGetter() {
          if ( ! this.hasOwnPrivate_(name) ) {
            var parent = this;
            var c      = Object.create(cls);

            c.create = function innerClassCreate(args, ctx) {
              return cls.create(args, ctx || parent);
            };
            this.setPrivate_(name, c);
          }

          return this.getPrivate_(name);
        },
        configurable: true,
        enumerable: false
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',
  properties: [
    {
      class: 'AxiomArray',
      of: 'InnerClass',
      name: 'classes',
      // A custom adaptArrayElement is needed because we're
      // passing the model definition as model:, rather than
      // as all of the arguments to create().
      adaptArrayElement: function(o) {
        return foam.core.InnerClass.isInstance(o) ?
          o :
          foam.core.InnerClass.create({model: o}) ;
      }
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.pm',
  name: 'PMInfo',

  documentation: 'Performance Measurement database entry.',

  ids: [ 'clsname', 'pmname' ],

  properties: [
    {
      class: 'String',
      name: 'clsname',
      label: 'Class'
    },
    {
      class: 'String',
      name: 'pmname',
      label: 'Name'
    },
    {
      class: 'Int',
      name: 'numoccurrences',
      label: 'Count'
    },
    {
      class: 'Long',
      name: 'mintime',
      label: 'Min'
    },
    {
      class: 'Long',
      name: 'average',
      label: 'Avg',
      getter: function() { return (this.totaltime / this.numoccurrences).toFixed(2); },
      javaGetter: `return (long) Math.round( ( getTotaltime() / getNumoccurrences() ) * 100 ) / 100;`,
      transient: true
    },
    {
      class: 'Long',
      name: 'maxtime',
      label: 'Max'
    },
    {
      class: 'Long',
      name: 'totaltime',
      label: 'Total',
      tableCellFormatter: function(value) {
        this.tag({class: 'foam.nanos.pm.TemperatureCView'}).add(' ', value);
      }
    }
  ]
});
<commit_msg>Set PMInfo temperature.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.pm',
  name: 'PMInfo',

  documentation: 'Performance Measurement database entry.',

  ids: [ 'clsname', 'pmname' ],

  properties: [
    {
      class: 'String',
      name: 'clsname',
      label: 'Class'
    },
    {
      class: 'String',
      name: 'pmname',
      label: 'Name'
    },
    {
      class: 'Int',
      name: 'numoccurrences',
      label: 'Count'
    },
    {
      class: 'Long',
      name: 'mintime',
      label: 'Min'
    },
    {
      class: 'Long',
      name: 'average',
      label: 'Avg',
      getter: function() { return (this.totaltime / this.numoccurrences).toFixed(2); },
      javaGetter: `return (long) Math.round( ( getTotaltime() / getNumoccurrences() ) * 100 ) / 100;`,
      transient: true
    },
    {
      class: 'Long',
      name: 'maxtime',
      label: 'Max'
    },
    {
      class: 'Long',
      name: 'totaltime',
      label: 'Total',
      tableCellFormatter: function(value) {
        this.tag({class: 'foam.nanos.pm.TemperatureCView', totalTime: value}).add(' ', value);
      }
    }
  ]
});
<|endoftext|>


<commit_before>"use strict";

try {
	var config = require("./config.json");
} catch (e) {
	console.log("Could not load `config.json`. Did you copy `config.example.json` to `config.json` and replaced the credentials?");
	process.exit(1);
}

try {
	var pogobuf = require("pogobuf"),
	POGOProtos = require("node-pogo-protos"),
	nodeGeocoder = require("node-geocoder"),
	log = require("./log"),
	blinkstick = require("blinkstick"),
	Redis = require("ioredis"),
	os = require("os"),
	waterfall = require("async-waterfall");
} catch (e) {
	console.log("Could not load all modules. Did you run `npm install`?", e);
	process.exit(1);
}

log("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");
log("*                                                                     *");
log("*                        H e l l o   t h e r e                        *");
log("*                                                                     *");
log("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");

var login = new pogobuf.PTCLogin(),
	client = new pogobuf.Client(),
	geocoder = nodeGeocoder(),
	lat,
	lng,
	intervalId;

/**
 * Blinkstick
 */
var led = {
	stick: null,
	isConnected: function isConnected() {
		if (!stick) {
			// try finding the blinkstick again if wasn"t connected at first
			led.stick = blinkstick.findFirst();
		}
		return (typeof stick !== "undefined");
	},
	blinker: function(team) {
		if (this.isConnected()) {
			var color = config.team_colors[team];
			log("** BLINKER ** Pulsing to " + color);

			waterfall([
				function(done) {
					led.stick.pulse(color, done);
				},
				function(done) {
					led.stick.pulse(color, done);
				},
				function(done) {
					setTimeout(function(){
						done();
					}, 1500);
				},
				function(done) {
					stick.setColor(color, done);
				},
			], function(err, result) {
				if (err) {
					return log("** BLINKER ** Encountered error!", err);
				}
				return log("** BLINKER ** Done pulsing!");
			});
		}
	}

};

var getGymDetails = function() {

	// batch, plz
	// client.batchStart();
	// client.getGymDetails(config.watched_gym.id, config.watched_gym.latitude, config.watched_gym.longitude);
	// client.batchCall()
	client.getGymDetails(config.watched_gym.id, config.watched_gym.latitude, config.watched_gym.longitude)
	.then(gym => {

		// Display gym information
		var fortData = gym.gym_state.fort_data,
			memberships = gym.gym_state.memberships,
			out = [];

		out.push("Gym: " + gym.name);
		out.push(
			"Owner: " +
			pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.TeamColor, fortData.owned_by_team) +
			((fortData.is_in_battle) ? " [IN BATTLE]" : "")
		);

		if (config.watched_gym.verbose) {
			out.push("Points: " + fortData.gym_points);
			if (memberships && memberships.length) {
				var highest = memberships[memberships.length - 1];
				out.push("Highest Pokémon: " + pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.PokemonId, highest.pokemon_data.pokemon_id) + ", " + highest.pokemon_data.cp + " CP");
				out.push("Trainer: " + highest.trainer_public_profile.name + ", level " + highest.trainer_public_profile.level);
			}
		}

		log(out.join("\t"));

		if (fortData.owned_by_team !== config.watched_gym.owned_by_team) {
			// victory!
			var previous_owner = pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.TeamColor, config.watched_gym.owned_by_team),
				new_owner = pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.TeamColor, fortData.owned_by_team);
			if (config.watched_gym.owned_by_team !== 0) {
				log("VICTORY!! " + previous_owner + " lost to " + new_owner + "!!");
			}
			led.blinker(fortData.owned_by_team);
			config.watched_gym.owned_by_team = fortData.owned_by_team;
		}
	})
	.catch((err) => {
		console.log("ERROR: ", err.message);
		clearInterval(intervalId);
		doLogin();
	});
};

var doLogin = function() {
	log("Logging in..");
	login.login(config.username, config.password)
		.then(token => {
			// Initialize the client
			log("Setting auth & position..");
			client.setAuthInfo(config.provider, token);
			client.setPosition(config.location.lat, config.location.lng);

			// Perform the initial request
			return client.init();
		})

		.then(() => {

			log("Successfully logged in");
			console.log();

			getGymDetails();
			intervalId = setInterval(getGymDetails, config.query_interval);

		})

}

// start up blinkstick so we know when project is deployed to the raspberry pi
if (led.stick) led.stick.morph("green");

// Start up main app logic
doLogin();

/**
 * do graceful shutdown when told to
 */
process.on("SIGINT", function() {
	log("*** Received request to stop poky ***");

	clearInterval(intervalId);

	if (led.stick) {
		led.stick.setColor(0, 0, 0, function(){
			process.exit(0);
		});
	} else {
		process.exit(0);
	}
});
<commit_msg>More errors in scope.<commit_after>"use strict";

try {
	var config = require("./config.json");
} catch (e) {
	console.log("Could not load `config.json`. Did you copy `config.example.json` to `config.json` and replaced the credentials?");
	process.exit(1);
}

try {
	var pogobuf = require("pogobuf"),
	POGOProtos = require("node-pogo-protos"),
	nodeGeocoder = require("node-geocoder"),
	log = require("./log"),
	blinkstick = require("blinkstick"),
	Redis = require("ioredis"),
	os = require("os"),
	waterfall = require("async-waterfall");
} catch (e) {
	console.log("Could not load all modules. Did you run `npm install`?", e);
	process.exit(1);
}

log("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");
log("*                                                                     *");
log("*                        H e l l o   t h e r e                        *");
log("*                                                                     *");
log("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");

var login = new pogobuf.PTCLogin(),
	client = new pogobuf.Client(),
	geocoder = nodeGeocoder(),
	lat,
	lng,
	intervalId;

/**
 * Blinkstick
 */
var led = {
	stick: null,
	isConnected: function isConnected() {
		if (!led.stick) {
			// try finding the blinkstick again if wasn"t connected at first
			led.stick = blinkstick.findFirst();
		}
		return (typeof led.stick !== "undefined");
	},
	blinker: function(team) {
		if (this.isConnected()) {
			var color = config.team_colors[team];
			log("** BLINKER ** Pulsing to " + color);

			waterfall([
				function(done) {
					led.stick.pulse(color, done);
				},
				function(done) {
					led.stick.pulse(color, done);
				},
				function(done) {
					setTimeout(function(){
						done();
					}, 1500);
				},
				function(done) {
					led.stick.setColor(color, done);
				},
			], function(err, result) {
				if (err) {
					return log("** BLINKER ** Encountered error!", err);
				}
				return log("** BLINKER ** Done pulsing!");
			});
		}
	}

};

var getGymDetails = function() {

	// batch, plz
	// client.batchStart();
	// client.getGymDetails(config.watched_gym.id, config.watched_gym.latitude, config.watched_gym.longitude);
	// client.batchCall()
	client.getGymDetails(config.watched_gym.id, config.watched_gym.latitude, config.watched_gym.longitude)
	.then(gym => {

		// Display gym information
		var fortData = gym.gym_state.fort_data,
			memberships = gym.gym_state.memberships,
			out = [];

		out.push("Gym: " + gym.name);
		out.push(
			"Owner: " +
			pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.TeamColor, fortData.owned_by_team) +
			((fortData.is_in_battle) ? " [IN BATTLE]" : "")
		);

		if (config.watched_gym.verbose) {
			out.push("Points: " + fortData.gym_points);
			if (memberships && memberships.length) {
				var highest = memberships[memberships.length - 1];
				out.push("Highest Pokémon: " + pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.PokemonId, highest.pokemon_data.pokemon_id) + ", " + highest.pokemon_data.cp + " CP");
				out.push("Trainer: " + highest.trainer_public_profile.name + ", level " + highest.trainer_public_profile.level);
			}
		}

		log(out.join("\t"));

		if (fortData.owned_by_team !== config.watched_gym.owned_by_team) {
			// victory!
			var previous_owner = pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.TeamColor, config.watched_gym.owned_by_team),
				new_owner = pogobuf.Utils.getEnumKeyByValue(POGOProtos.Enums.TeamColor, fortData.owned_by_team);
			if (config.watched_gym.owned_by_team !== 0) {
				log("VICTORY!! " + previous_owner + " lost to " + new_owner + "!!");
			}
			led.blinker(fortData.owned_by_team);
			config.watched_gym.owned_by_team = fortData.owned_by_team;
		}
	})
	.catch((err) => {
		console.log("ERROR: ", err.message);
		clearInterval(intervalId);
		doLogin();
	});
};

var doLogin = function() {
	log("Logging in..");
	login.login(config.username, config.password)
		.then(token => {
			// Initialize the client
			log("Setting auth & position..");
			client.setAuthInfo(config.provider, token);
			client.setPosition(config.location.lat, config.location.lng);

			// Perform the initial request
			return client.init();
		})

		.then(() => {

			log("Successfully logged in");
			console.log();

			getGymDetails();
			intervalId = setInterval(getGymDetails, config.query_interval);

		})

}

// start up blinkstick so we know when project is deployed to the raspberry pi
if (led.stick) led.stick.morph("green");

// Start up main app logic
doLogin();

/**
 * do graceful shutdown when told to
 */
process.on("SIGINT", function() {
	log("*** Received request to stop poky ***");

	clearInterval(intervalId);

	if (led.stick) {
		led.stick.setColor(0, 0, 0, function(){
			process.exit(0);
		});
	} else {
		process.exit(0);
	}
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.pm',
  name: 'PMInfo',

  documentation: 'Performance Measurement database entry.',

  ids: [ 'key', 'name' ],

  tableColumns: [ 'key', 'name', 'count', 'minTime', 'average', 'maxTime', 'totalTime' ],

  searchColumns: [ 'key', 'name' ],

  properties: [
    {
      class: 'String',
      name: 'key',
      label: 'Class',
      tableWidth: 170,
      tableCellFormatter: function(cls) {
        // strip out common prefixes to make easier to read in TableView
        this.add(cls.replace(/foam\./,'').replace(/dao\.|http\.|pool\.|boot\.|ruler\.|script\./,'').replace(/ThreadPoolAgency\$/,'').replace(/nanos\./,''));
      }
    },
    {
      class: 'String',
      name: 'name',
      tableWidth: 320
    },
    {
      class: 'Int',
      name: 'count',
      label: 'Count',
      tableWidth: 70
    },
    {
      class: 'Duration',
      name: 'minTime',
      label: 'Min'
    },
    {
      class: 'Duration',
      name: 'average',
      label: 'Avg',
      getter: function() { return (this.totalTime / this.count).toFixed(2); },
      javaGetter: `return (long) (Math.round( ( (float)getTotalTime() / (float)getCount() ) * 100.0 ) / 100.0);`,
      storageTransient: true
    },
    {
      class: 'Duration',
      name: 'maxTime',
      label: 'Max'
    },
    {
      class: 'Long',
      name: 'totalTime',
      label: 'Total',
      tableCellFormatter: { class: 'foam.nanos.pm.PMTemperatureCellFormatter' }
    },
    {
      class: 'Boolean',
      name: 'capture'
    },
    {
      class: 'Code',
      name: 'captureTrace',
      readPermissionRequired: true,
      writePermissionRequired: true
    }
  ]
});
<commit_msg>Add fold and reduce to PMInfo.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.pm',
  name: 'PMInfo',

  documentation: 'Performance Measurement database entry.',

  ids: [ 'key', 'name' ],

  tableColumns: [ 'key', 'name', 'count', 'minTime', 'average', 'maxTime', 'totalTime' ],

  searchColumns: [ 'key', 'name' ],

  properties: [
    {
      class: 'String',
      name: 'key',
      label: 'Class',
      tableWidth: 170,
      tableCellFormatter: function(cls) {
        // strip out common prefixes to make easier to read in TableView
        this.add(cls.replace(/foam\./,'').replace(/dao\.|http\.|pool\.|boot\.|ruler\.|script\./,'').replace(/ThreadPoolAgency\$/,'').replace(/nanos\./,''));
      }
    },
    {
      class: 'String',
      name: 'name',
      tableWidth: 320
    },
    {
      class: 'Int',
      name: 'count',
      label: 'Count',
      tableWidth: 70
    },
    {
      class: 'Duration',
      name: 'minTime',
      label: 'Min'
    },
    {
      class: 'Duration',
      name: 'average',
      label: 'Avg',
      getter: function() { return (this.totalTime / this.count).toFixed(2); },
      javaGetter: `return (long) (Math.round( ( (float)getTotalTime() / (float)getCount() ) * 100.0 ) / 100.0);`,
      storageTransient: true
    },
    {
      class: 'Duration',
      name: 'maxTime',
      label: 'Max'
    },
    {
      class: 'Long',
      name: 'totalTime',
      label: 'Total',
      tableCellFormatter: { class: 'foam.nanos.pm.PMTemperatureCellFormatter' }
    },
    {
      class: 'Boolean',
      name: 'capture'
    },
    {
      class: 'Code',
      name: 'captureTrace',
      readPermissionRequired: true,
      writePermissionRequired: true
    }
  ],

  methods: [
    {
      name: 'fold',
      type: 'void',
      args: [ 'PM pm' ],
      javaCode: `
      if ( pm.getTime() < getMinTime() ) setMinTime(pm.getTime());
      if ( pm.getTime() > getMaxTime() ) setMaxTime(pm.getTime());

      setCount(getCount() + 1);
      setTotalTime(getTotalTime() + pm.getTime());
      `
    },
    {
      name: 'reduce',
      type: 'void',
      args: [ 'PMInfo other' ],
      javaCode: `
        setCount(getCount() + other.getCount());
        setTotalTime(getTotalTime() + other.getTotalTime());
        setMinTime(Math.min(getMinTime(), other.getMinTime()));
        setMinTime(Math.max(getMaxTime(), other.getMaxTime()));
        if ( foam.util.SafetyUtil.isEmpty(other.getCaptureTrace()) ) {
          setCaptureTrace(other.getCaptureTrace());
          setCapture(false);
        }
      `
    }
  ]
});
<|endoftext|>


<commit_before>import on from 'on'

export default class GameScene {

  constructor (step = 'default') {
  	this.step = step

    this.init()
  }

  init () {
    this.onEvent = on(this)
  }

  start () {
    this.btnNext = document.querySelector('.btn-next')
    this._handleEnd = this.end.bind(this)
    this.btnNext.addEventListener('click', this._handleEnd)
  }

  end () {
      if(this.btnNext != null) this.btnNext.removeEventListener('click', this._handleEnd)
    this.onEvent._fire({event: 'sceneEnd', step: this.step})
  }

  get html () {
    return `

              Current scene is : ${this.step}

                <button class="btn-next">Next</button>

`
  }
}<commit_msg>test prettier<commit_after>import on from 'on'

export default class GameScene {

  constructor (step = 'default') {
  	this.step = step

    this.init()
  }

  init () {
    this.onEvent = on(this)
  }

  start () {
    this.btnNext = document.querySelector('.btn-next')
    this._handleEnd = this.end.bind(this)
    this.btnNext.addEventListener('click', this._handleEnd)
  }

  end () {
    if(this.btnNext != null) this.btnNext.removeEventListener('click', this._handleEnd)
    this.onEvent._fire({event: 'sceneEnd', step: this.step})
  }

  get html () {
    return `

              Current scene is : ${this.step}

                <button class="btn-next">Next</button>

`
  }
}<|endoftext|>


<commit_before>var _ = require('underscore');
var config = require('../../cdb.config');
var LayerModelBase = require('./layer-model-base');
var InfowindowTemplate = require('./infowindow-template');
var TooltipTemplate = require('./tooltip-template');
var Legends = require('./legends/legends');
window.LayerGroupCollection = {};
var ATTRIBUTES_THAT_TRIGGER_VIS_RELOAD = ['sql', 'source', 'sql_wrap', 'cartocss'];

var RasterLayer = LayerModelBase.extend({
  defaults: {
    type: 'Raster',
    attribution: config.get('cartodb_attributions')
  },

  initialize: function (attrs, options) {
    attrs = attrs || {};
    options = options || {};
    if (!options.engine) throw new Error('engine is required');

    this._engine = options.engine;
    if (attrs && attrs.cartocss) {
      this.set('initialStyle', attrs.cartocss);
    }

    // Hopefully in the future (@apercas) => legends ✓
      // PUBLIC PROPERTIES
      this.infowindow = new InfowindowTemplate(attrs.infowindow);
      this.tooltip = new TooltipTemplate(attrs.tooltip);
      this.unset('infowindow');
      this.unset('tooltip');

      this.legends = new Legends(attrs.legends, {
        engine: this._engine
      });
      this.unset('legends');

      this.bind('change', this._onAttributeChanged, this);
      this.infowindow.fields.bind('reset add remove', this._reloadVis, this);
      this.tooltip.fields.bind('reset add remove', this._reloadVis, this);

    LayerModelBase.prototype.initialize.apply(this, arguments);
  },

  _onAttributeChanged: function () {
    var reloadVis = _.any(ATTRIBUTES_THAT_TRIGGER_VIS_RELOAD, function (attr) {
      if (this.hasChanged(attr)) {
        if (attr === 'cartocss' && this._dataProvider) {
          return false;
        }
        return true;
      }
    }, this);

    if (reloadVis) {
      this._reloadVis();
    }
  },

  _reloadVis: function () {
    this._engine.reload({
      sourceId: this.get('id')
    });
  },

  restoreCartoCSS: function () {
    this.set('cartocss', this.get('initialStyle'));
  },

  setOpacity: function (layer_name, opacity) {
    this.set('cartocss', "#" + attrs.layer_name + " {raster-opacity: " + opacity +"}");
  },

  isVisible: function () {
    return this.get('visible');
  },

  isInteractive: function () {
    // By default it has one field (cartodb_id)
    // return this.getInteractiveColumnNames().length > 1;
    return false;
  },
  _hasInfowindowFields: function () {
    return this.infowindow.hasFields();
  },

  _hasTooltipFields: function () {
    return this.tooltip.hasFields();
  },

  getGeometryType: function () {
    if (this._dataProvider) {
      var index = this._dataProvider._layerIndex;
      var sublayer = this._dataProvider._vectorLayerView.renderers[index];
      return sublayer.inferGeometryType();
    }
    return null;
  },

  getInteractiveColumnNames: function () {
    return ['cartodb_id'];
    return _.chain(['cartodb_id'])
      .union(this.infowindow.getFieldNames())
      .union(this.tooltip.getFieldNames())
      .uniq()
      .value();
  },

  isInfowindowEnabled: function () {
    return this.infowindow.hasTemplate();
  },

  isTooltipEnabled: function () {
    return this.tooltip.hasTemplate();
  },

  getName: function () {
    return this.get('layer_name');
  },

  setDataProvider: function (dataProvider) {
    this._dataProvider = dataProvider;
  },

  getDataProvider: function () {
    return this._dataProvider;
  },
});

module.exports = RasterLayer;<commit_msg>add source to raster<commit_after>var _ = require('underscore');
var config = require('../../cdb.config');
var LayerModelBase = require('./layer-model-base');
var InfowindowTemplate = require('./infowindow-template');
var TooltipTemplate = require('./tooltip-template');
var Legends = require('./legends/legends');
window.LayerGroupCollection = {};
var ATTRIBUTES_THAT_TRIGGER_VIS_RELOAD = ['sql', 'source', 'sql_wrap', 'cartocss'];

var RasterLayer = LayerModelBase.extend({
  defaults: {
    type: 'Raster',
    attribution: config.get('cartodb_attributions')
  },

  initialize: function (attrs, options) {
    attrs = attrs || {};
    options = options || {};
    if (!options.engine) throw new Error('engine is required');

    this._engine = options.engine;
    if (attrs && attrs.cartocss) {
      this.set('initialStyle', attrs.cartocss);
    }

    // Hopefully in the future (@apercas) => legends ✓
      // PUBLIC PROPERTIES
      this.infowindow = new InfowindowTemplate(attrs.infowindow);
      this.tooltip = new TooltipTemplate(attrs.tooltip);
      this.unset('infowindow');
      this.unset('tooltip');

      this.legends = new Legends(attrs.legends, {
        engine: this._engine
      });
      this.unset('legends');

      this.bind('change', this._onAttributeChanged, this);
      this.infowindow.fields.bind('reset add remove', this._reloadVis, this);
      this.tooltip.fields.bind('reset add remove', this._reloadVis, this);

    LayerModelBase.prototype.initialize.apply(this, arguments);
  },

  _onAttributeChanged: function () {
    var reloadVis = _.any(ATTRIBUTES_THAT_TRIGGER_VIS_RELOAD, function (attr) {
      if (this.hasChanged(attr)) {
        if (attr === 'cartocss' && this._dataProvider) {
          return false;
        }
        return true;
      }
    }, this);

    if (reloadVis) {
      this._reloadVis();
    }
  },

  _reloadVis: function () {
    this._engine.reload({
      sourceId: this.get('id')
    });
  },

  restoreCartoCSS: function () {
    this.set('cartocss', this.get('initialStyle'));
  },

  setOpacity: function (layer_name, opacity) {
    this.set('cartocss', "#" + attrs.layer_name + " {raster-opacity: " + opacity +"}");
  },

  isVisible: function () {
    return this.get('visible');
  },

  getSourceId: function () {
    var source = this.getSource();
    return source && source.id;
  },

  isInteractive: function () {
    // By default it has one field (cartodb_id)
    // return this.getInteractiveColumnNames().length > 1;
    return false;
  },
  _hasInfowindowFields: function () {
    return this.infowindow.hasFields();
  },

  _hasTooltipFields: function () {
    return this.tooltip.hasFields();
  },

  getGeometryType: function () {
    if (this._dataProvider) {
      var index = this._dataProvider._layerIndex;
      var sublayer = this._dataProvider._vectorLayerView.renderers[index];
      return sublayer.inferGeometryType();
    }
    return null;
  },

  getInteractiveColumnNames: function () {
    return ['cartodb_id'];
    return _.chain(['cartodb_id'])
      .union(this.infowindow.getFieldNames())
      .union(this.tooltip.getFieldNames())
      .uniq()
      .value();
  },

  isInfowindowEnabled: function () {
    return this.infowindow.hasTemplate();
  },

  isTooltipEnabled: function () {
    return this.tooltip.hasTemplate();
  },

  getName: function () {
    return this.get('layer_name');
  },

  setDataProvider: function (dataProvider) {
    this._dataProvider = dataProvider;
  },

  getDataProvider: function () {
    return this._dataProvider;
  },
});

module.exports = RasterLayer;<|endoftext|>


<commit_before>/**
 * @file 获取代理相关的信息
 * @author zdying
 */

var url = require('url');
var path = require('path');

var execDirectives = require('../directives').execDirectives;
var Transform = require('hiproxy-conf-parser').Transform;
var utils = require('../helpers/utils');
var clone = utils.clone;
var parseCookie = utils.parseCookie;
var parseQueryString = utils.parseQueryString;

/**
 * 获取代理信息, 用于请求代理的地址
 * @param {Object}  request 请求对象
 * @param {Object}  hostsRules 解析后的hosts规则
 * @param {Array}   rewriteRules 解析后的rewrite规则
 * @private
 * @ignore
 * @returns {Object}
 */
module.exports = function getProxyInfo (request, hostsRules, rewriteRules) {
  var originUrl = request.url;
  var uri = url.parse(originUrl);
  var rewrite = !!rewriteRules && getRewriteRule(uri, rewriteRules);
  var host = !!hostsRules && hostsRules[uri.hostname];
  var hostname, port, path, proxyName, protocol;
  var proxyInfo = {
    headers: request.headers
  };
  var proxyType = 'DIRECT';

  protocol = uri.protocol;

  // rewrite 优先级高于 hosts
  if (rewrite /* && rewrite.props.proxy */) {
    // 根据请求信息，设置内置变量
    setBuiltInVars(rewrite, request);
    // 再次替换变量的值
    Transform.replaceVar(rewrite, rewrite.variables, ['extends']);

    var rewriteProps = rewrite.variables;
    var proxyPass = rewriteProps.$proxy_pass || '';
    var isBaseRule = rewrite.isBaseRule;
    var alias = rewriteProps.$alias;
    var proxyUrlObj = url.parse(proxyPass);
    var protocolReg = /^(\w+:\/\/)/;
    var newUrl = '';
    var newUrlObj;
    var isLocRegExp = isRegExp(rewrite.location);

    // 如果代理地址中包含具体协议，删除原本url中的协议
    // 最终替换位代理地址的协议
    if (!alias && !isBaseRule && proxyUrlObj.protocol) {
      protocol = proxyUrlObj.protocol;
      originUrl = originUrl.replace(protocolReg, '');
    }

    if (proxyPass) {
      // 将原本url中的部分替换为代理地址
      if (isLocRegExp) {
        // 如果是正则表达式，直接食用proxy_pass的值
        newUrl = proxyPass;
      } else {
        // 普通地址字符串
        // 否则，把url中的source部分替换成proxy
        newUrl = originUrl.replace(rewrite.extends.domain + rewrite.location, proxyPass);
      }
    }

    var context = {
      req: request,
      proxy: proxyInfo,
      rewriteRule: rewrite
    };

    log.debug('newURL ==>', newUrl);
    log.debug('alias  ==>', alias);

    // pre-request
    // set default `Host` header
    if (proxyUrlObj.host) {
      proxyInfo.headers.host = proxyUrlObj.host;
    }
    // pre-request

    execDirectives(rewrite, context, 'request');

    if (alias) {
      // 本地文件系统路径, 删除前面的协议部分
      newUrl = newUrl.replace(/^(\w+:\/\/)/, '').split('?')[0];
      proxyType = 'ALIAS';
    } else {
      newUrlObj = url.parse(newUrl);

      hostname = newUrlObj.hostname || '';
      port = newUrlObj.port;
      path = newUrlObj.path;
      proxyType = proxyPass ? 'REWRITE' : 'DIRECTIVE';
    }

    proxyName = 'hiproxy';
  } else if (host) {
    // TODO 这里的协议，到底应该用什么?
    protocol = 'http:';
    hostname = host.split(':')[0];
    // port = (protocol === 'https:') ? 443 : Number(uri.port || host.split(':')[1]);
    port = host.split(':')[1] || uri.port || (protocol === 'https:' ? 443 : 80);
    path = uri.path;
    proxyName = 'hiproxy';
    proxyType = 'HOSTS';
  } else {
    hostname = uri.hostname;
    port = uri.port;
    path = uri.path;
  }

  request.headers['accept-encoding'] = 'gzip,deflate';
  // TODO add test case.
  // correct the `Content-Length` value.
  if ('content-length' in request.headers) {
    request.headers['content-length'] = Buffer.byteLength(request.body || '');
  }

  var _proxyInfo = {
    hostname: hostname,
    port: port,
    path: path,
    method: proxyInfo.method || request.method,
    // headers: request.headers,
    protocol: protocol,

    proxyType: proxyType,
    proxyPass: proxyPass,
    PROXY: proxyName,
    hostsRule: host,
    hostName: uri.hostname,
    rewriteRule: rewrite,
    alias: alias,
    url: newUrl
  };

  for (var key in _proxyInfo) {
    proxyInfo[key] = _proxyInfo[key];
  }

  return proxyInfo;
};

/**
 * 根据url查找对应的rewrite规则
 * @param urlObj
 * @param rewriteRules
 * @param domainCache
 * @returns {*}
 */
function getRewriteRule (urlObj, rewriteRules) {
  var hostname = urlObj.hostname;
  var href = urlObj.href;
  var rewriteRule = null;
  var lastDeep = -1;

  var domains = rewriteRules[hostname];

  if (!domains || !Array.isArray(domains)) {
    return null;
  }

  domains.forEach(function (domain) {
    var rule = domain;
    var locations = rule.locations;
    var urlPath = urlObj.path;
    var loc = null;
    var currentDeep = 0;
    var location = '';
    var isLocRegExp = false;

    for (var i = 0, len = locations.length; i < len; i++) {
      loc = locations[i];
      location = loc.location;
      isLocRegExp = isRegExp(location);

      log.debug('getRewriteRule - current location path =>'.green, String(location).bold.green);
      log.debug('getRewriteRule - current url path =>'.green, urlPath.green);

      if (isLocRegExp) {
        /** 正则表达式 **/
        // var reg = toRegExp(locPath, 'i');

        if (location.test(urlPath)) {
          currentDeep = location.source.replace(/^\\?\/|\\?\/$/g, '').split(/\\?\//).length;

          if (currentDeep > lastDeep) {
            rewriteRule = loc;
          }

          log.debug(
            'getRewriteRule -',
            'regexp match =>', urlPath.match(location),
            'deep =>', String(currentDeep).bold.green,
            'last deep =>', String(lastDeep).bold.green,
            'should replace last rule =>', String(currentDeep > lastDeep).bold.green
          );
        }
      } else if (urlPath.indexOf(location) === 0) {
        /** 非正则表达式 **/
        // 如果url中path以location中的path开头
        currentDeep = location.replace(/^\/|\/$/g, '').split('/').length;

        // 如果是'/', 长度设置为0
        if (currentDeep === 1 && location === '/') {
          currentDeep = 0;
        }

        log.debug('getRewriteRule -', 'get rewrite rule for url =>', urlObj.href.bold.green);
        log.debug(
          'getRewriteRule -',
          'current match location.path =>', String(location).bold.green,
          'deep =>', String(currentDeep).bold.green,
          'last deep =>', String(lastDeep).bold.green,
          'should replace last rule =>', String(currentDeep > lastDeep).bold.green
        );

        // 如果匹配的深度比上一次匹配的深度深（比上次匹配更精确）
        // 替换成新匹配到的规则
        if (currentDeep > lastDeep) {
          rewriteRule = loc;
          lastDeep = currentDeep;
        }
      }
    }
  });

  log.debug('getProxyInfo -'.red, href, '==>', utils.toJSON(rewriteRule));

  return clone(rewriteRule);
}

/**
 * 设置内置变量
 *
 * @param {Object} rewrite 当前请求匹配的rewrite规则
 * @param {http.IncomingMessage} request 当前请求对象
 */
function setBuiltInVars (rewrite, request) {
  var variables = rewrite.variables;
  var urlObj = url.parse(request.url);
  var headers = request.headers;
  var cookies = parseCookie(headers.cookie);
  var queryObj = parseQueryString(urlObj.search);
  var tmpKey = '';
  var isLocRegExp = isRegExp(rewrite.location);
  var headerHost = headers.host || '';

  var vars = {
    $host: urlObj.host || headerHost,
    $hostname: urlObj.hostname || headerHost.split(':')[0],
    $search: urlObj.search || '',
    $query_string: urlObj.query || '',
    $server_port: urlObj.port || '80',
    $scheme: (urlObj.protocol || '').replace(':', ''),
    $request_uri: urlObj.href,
    $path: urlObj.path,
    $path_name: urlObj.pathname || '',
    $base_name: path.basename(urlObj.pathname),
    $dir_name: path.dirname(urlObj.pathname),
    $hash: urlObj.hash || '',
    $hash_value: (urlObj.hash || '').replace('#', ''),
    $uri: urlObj.href
  };

  // $cookie_name
  for (var cookie in cookies) {
    tmpKey = cookie.toLowerCase().replace(/-/, '_');
    vars['$cookie_' + tmpKey] = cookies[cookie] || '';
  }

  // $http_name
  // arbitrary request header field; the last part of a variable name is the field name
  // converted to lower case with dashes replaced by underscores
  for (var header in headers) {
    tmpKey = header.toLowerCase().replace(/-/, '_');
    vars['$http_' + tmpKey] = headers[header] || '';
  }

  // $arg_name query string value
  for (var arg in queryObj) {
    tmpKey = arg.toLowerCase().replace(/-/, '_');
    vars['$arg_' + tmpKey] = queryObj[arg] || '';
  }

  if (isLocRegExp) {
    // RegExp location
    var urlMatch = urlObj.path.match(rewrite.location);
    if (Array.isArray(urlMatch)) {
      for (var i = 1; i < 9; i++) {
        vars['$' + i] = urlMatch[i] || '';
      }
    }
  }

  for (var key in vars) {
    variables[key] = vars[key];
  }
}

function isRegExp (location) {
  return Object.prototype.toString.call(location) === '[object RegExp]';
}
<commit_msg>delete unused variables<commit_after>/**
 * @file 获取代理相关的信息
 * @author zdying
 */

var url = require('url');
var path = require('path');

var execDirectives = require('../directives').execDirectives;
var Transform = require('hiproxy-conf-parser').Transform;
var utils = require('../helpers/utils');
var clone = utils.clone;
var parseCookie = utils.parseCookie;
var parseQueryString = utils.parseQueryString;

/**
 * 获取代理信息, 用于请求代理的地址
 * @param {Object}  request 请求对象
 * @param {Object}  hostsRules 解析后的hosts规则
 * @param {Array}   rewriteRules 解析后的rewrite规则
 * @private
 * @ignore
 * @returns {Object}
 */
module.exports = function getProxyInfo (request, hostsRules, rewriteRules) {
  var originUrl = request.url;
  var uri = url.parse(originUrl);
  var rewrite = !!rewriteRules && getRewriteRule(uri, rewriteRules);
  var host = !!hostsRules && hostsRules[uri.hostname];
  var hostname, port, path, protocol;
  var proxyInfo = {
    headers: request.headers
  };
  var proxyType = 'DIRECT';

  protocol = uri.protocol;

  // rewrite 优先级高于 hosts
  if (rewrite /* && rewrite.props.proxy */) {
    // 根据请求信息，设置内置变量
    setBuiltInVars(rewrite, request);
    // 再次替换变量的值
    Transform.replaceVar(rewrite, rewrite.variables, ['extends']);

    var rewriteProps = rewrite.variables;
    var proxyPass = rewriteProps.$proxy_pass || '';
    var isBaseRule = rewrite.isBaseRule;
    var alias = rewriteProps.$alias;
    var proxyUrlObj = url.parse(proxyPass);
    var protocolReg = /^(\w+:\/\/)/;
    var newUrl = '';
    var newUrlObj;
    var isLocRegExp = isRegExp(rewrite.location);

    // 如果代理地址中包含具体协议，删除原本url中的协议
    // 最终替换位代理地址的协议
    if (!alias && !isBaseRule && proxyUrlObj.protocol) {
      protocol = proxyUrlObj.protocol;
      originUrl = originUrl.replace(protocolReg, '');
    }

    if (proxyPass) {
      // 将原本url中的部分替换为代理地址
      if (isLocRegExp) {
        // 如果是正则表达式，直接食用proxy_pass的值
        newUrl = proxyPass;
      } else {
        // 普通地址字符串
        // 否则，把url中的source部分替换成proxy
        newUrl = originUrl.replace(rewrite.extends.domain + rewrite.location, proxyPass);
      }
    }

    var context = {
      req: request,
      proxy: proxyInfo,
      rewriteRule: rewrite
    };

    log.debug('newURL ==>', newUrl);
    log.debug('alias  ==>', alias);

    // pre-request
    // set default `Host` header
    if (proxyUrlObj.host) {
      proxyInfo.headers.host = proxyUrlObj.host;
    }
    // pre-request

    execDirectives(rewrite, context, 'request');

    if (alias) {
      // 本地文件系统路径, 删除前面的协议部分
      newUrl = newUrl.replace(/^(\w+:\/\/)/, '').split('?')[0];
      proxyType = 'ALIAS';
    } else {
      newUrlObj = url.parse(newUrl);

      hostname = newUrlObj.hostname || '';
      port = newUrlObj.port;
      path = newUrlObj.path;
      proxyType = proxyPass ? 'REWRITE' : 'DIRECTIVE';
    }
  } else if (host) {
    // TODO 这里的协议，到底应该用什么?
    protocol = 'http:';
    hostname = host.split(':')[0];
    // port = (protocol === 'https:') ? 443 : Number(uri.port || host.split(':')[1]);
    port = host.split(':')[1] || uri.port || (protocol === 'https:' ? 443 : 80);
    path = uri.path;
    proxyType = 'HOSTS';
  } else {
    hostname = uri.hostname;
    port = uri.port;
    path = uri.path;
  }

  request.headers['accept-encoding'] = 'gzip,deflate';
  // TODO add test case.
  // correct the `Content-Length` value.
  if ('content-length' in request.headers) {
    request.headers['content-length'] = Buffer.byteLength(request.body || '');
  }

  var _proxyInfo = {
    hostname: hostname,
    port: port,
    path: path,
    method: proxyInfo.method || request.method,
    // headers: request.headers,
    protocol: protocol,

    proxyType: proxyType,
    proxyPass: proxyPass,
    hostsRule: host,
    hostName: uri.hostname,
    rewriteRule: rewrite,
    alias: alias,
    url: newUrl
  };

  for (var key in _proxyInfo) {
    proxyInfo[key] = _proxyInfo[key];
  }

  return proxyInfo;
};

/**
 * 根据url查找对应的rewrite规则
 * @param urlObj
 * @param rewriteRules
 * @param domainCache
 * @returns {*}
 */
function getRewriteRule (urlObj, rewriteRules) {
  var hostname = urlObj.hostname;
  var href = urlObj.href;
  var rewriteRule = null;
  var lastDeep = -1;

  var domains = rewriteRules[hostname];

  if (!domains || !Array.isArray(domains)) {
    return null;
  }

  domains.forEach(function (domain) {
    var rule = domain;
    var locations = rule.locations;
    var urlPath = urlObj.path;
    var loc = null;
    var currentDeep = 0;
    var location = '';
    var isLocRegExp = false;

    for (var i = 0, len = locations.length; i < len; i++) {
      loc = locations[i];
      location = loc.location;
      isLocRegExp = isRegExp(location);

      log.debug('getRewriteRule - current location path =>'.green, String(location).bold.green);
      log.debug('getRewriteRule - current url path =>'.green, urlPath.green);

      if (isLocRegExp) {
        /** 正则表达式 **/
        // var reg = toRegExp(locPath, 'i');

        if (location.test(urlPath)) {
          currentDeep = location.source.replace(/^\\?\/|\\?\/$/g, '').split(/\\?\//).length;

          if (currentDeep > lastDeep) {
            rewriteRule = loc;
          }

          log.debug(
            'getRewriteRule -',
            'regexp match =>', urlPath.match(location),
            'deep =>', String(currentDeep).bold.green,
            'last deep =>', String(lastDeep).bold.green,
            'should replace last rule =>', String(currentDeep > lastDeep).bold.green
          );
        }
      } else if (urlPath.indexOf(location) === 0) {
        /** 非正则表达式 **/
        // 如果url中path以location中的path开头
        currentDeep = location.replace(/^\/|\/$/g, '').split('/').length;

        // 如果是'/', 长度设置为0
        if (currentDeep === 1 && location === '/') {
          currentDeep = 0;
        }

        log.debug('getRewriteRule -', 'get rewrite rule for url =>', urlObj.href.bold.green);
        log.debug(
          'getRewriteRule -',
          'current match location.path =>', String(location).bold.green,
          'deep =>', String(currentDeep).bold.green,
          'last deep =>', String(lastDeep).bold.green,
          'should replace last rule =>', String(currentDeep > lastDeep).bold.green
        );

        // 如果匹配的深度比上一次匹配的深度深（比上次匹配更精确）
        // 替换成新匹配到的规则
        if (currentDeep > lastDeep) {
          rewriteRule = loc;
          lastDeep = currentDeep;
        }
      }
    }
  });

  log.debug('getProxyInfo -'.red, href, '==>', utils.toJSON(rewriteRule));

  return clone(rewriteRule);
}

/**
 * 设置内置变量
 *
 * @param {Object} rewrite 当前请求匹配的rewrite规则
 * @param {http.IncomingMessage} request 当前请求对象
 */
function setBuiltInVars (rewrite, request) {
  var variables = rewrite.variables;
  var urlObj = url.parse(request.url);
  var headers = request.headers;
  var cookies = parseCookie(headers.cookie);
  var queryObj = parseQueryString(urlObj.search);
  var tmpKey = '';
  var isLocRegExp = isRegExp(rewrite.location);
  var headerHost = headers.host || '';

  var vars = {
    $host: urlObj.host || headerHost,
    $hostname: urlObj.hostname || headerHost.split(':')[0],
    $search: urlObj.search || '',
    $query_string: urlObj.query || '',
    $server_port: urlObj.port || '80',
    $scheme: (urlObj.protocol || '').replace(':', ''),
    $request_uri: urlObj.href,
    $path: urlObj.path,
    $path_name: urlObj.pathname || '',
    $base_name: path.basename(urlObj.pathname),
    $dir_name: path.dirname(urlObj.pathname),
    $hash: urlObj.hash || '',
    $hash_value: (urlObj.hash || '').replace('#', ''),
    $uri: urlObj.href
  };

  // $cookie_name
  for (var cookie in cookies) {
    tmpKey = cookie.toLowerCase().replace(/-/, '_');
    vars['$cookie_' + tmpKey] = cookies[cookie] || '';
  }

  // $http_name
  // arbitrary request header field; the last part of a variable name is the field name
  // converted to lower case with dashes replaced by underscores
  for (var header in headers) {
    tmpKey = header.toLowerCase().replace(/-/, '_');
    vars['$http_' + tmpKey] = headers[header] || '';
  }

  // $arg_name query string value
  for (var arg in queryObj) {
    tmpKey = arg.toLowerCase().replace(/-/, '_');
    vars['$arg_' + tmpKey] = queryObj[arg] || '';
  }

  if (isLocRegExp) {
    // RegExp location
    var urlMatch = urlObj.path.match(rewrite.location);
    if (Array.isArray(urlMatch)) {
      for (var i = 1; i < 9; i++) {
        vars['$' + i] = urlMatch[i] || '';
      }
    }
  }

  for (var key in vars) {
    variables[key] = vars[key];
  }
}

function isRegExp (location) {
  return Object.prototype.toString.call(location) === '[object RegExp]';
}
<|endoftext|>


<commit_before>// ==========================================================================
//                            DG.CellAxisView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph/axes/axis_view');

/** @class  DG.CellAxisView - The view for a graph axis that displays cells.

 @extends DG.AxisView
 */
DG.CellAxisView = DG.AxisView.extend( (function() {
  var kTickLength = 4,
      kAxisGap = 2;

  return {
    /** @scope DG.CellAxisView.prototype */

    /**
     Number of pixels required from the axis line to the edge of the graph view
     @property { Number }
     */
    desiredExtent: function() {
      var tExtent = sc_super();
      if( !this.get('isNumeric')) { // Not yet handling numeric axis broken up by cells
        tExtent += kTickLength + kAxisGap + this.get('maxLabelExtent');
      }
      return tExtent;
    }.property('maxLabelExtent'),

    /**
     I'm not supposed to work with numbers.
     @property { Boolean }
     */
    isNumeric: false,

    /**
     * @property {Boolean}
     */
    centering: false,

    /**
     @property{Number}
     */
    axisLineCoordinate: function() {
      var tCoord;
      switch( this.get('orientation')) {
        case 'vertical':
          tCoord = this.get('drawWidth');
          break;
        case 'vertical2':
        case 'horizontal':
          tCoord = 0;
          break;
      }
      return tCoord;
    }.property('drawWidth'),

    /**
     @property {Number} Each time we draw the axis, we set this property to the maximum width
     of the label strings. Note that this will change depending not only on the length of the
     strings but on the rotation of the labels.
     */
    maxLabelExtent: 0,

    /**
     This is the main backbone of the axis.
     @return {Raphael element}
     */
    renderAxisLine: function() {
      var tCoord = this.get('axisLineCoordinate'),
          tPixelMin = this.get('pixelMin'),
          tPixelMax = this.get('pixelMax'),
          tStart, tStop;
      switch( this.get('orientation')) {
        case 'vertical':
          tStart = { x: tCoord - 1, y: tPixelMin };
          tStop = { x: tCoord - 1, y: tPixelMax };
          break;
        case 'horizontal':
          tStart = { x: tPixelMin, y: tCoord + 1 };
          tStop = { x: tPixelMax, y: tCoord + 1 };
          break;
        case 'vertical2':
          tStart = { x: tCoord + 1, y: tPixelMin };
          tStop = { x: tCoord + 1, y: tPixelMax };
          break;
      }
      return this._paper.line( tStart.x, tStart.y, tStop.x, tStop.y)
        .attr( { stroke: DG.PlotUtilities.kAxisColor,
          strokeWidth: 2 });
    },

    /**
     The convention is that, unlike continuous axes, for a
     vertical axis, the low numbers are at the top.
     @param {Number} in screen coordinates
     @return {Number} the cell number corresponding to the given screen coordinate
     */
    whichCell: function( iCoord) {
      var tNumCells = this.getPath('model.numberOfCells'),
          tCell = 0;
      if( tNumCells > 1) {
        var tPixelMin = this.get('pixelMin'),
            tPixelMax = this.get('pixelMax');
        // In order to do the computation, we have to adjust for the coordinates of the ends
        // of the axes, knowing the thePixel is in plot view coordinates, not axis view coordinates.
        if( this.get('isVertical')) {
          iCoord = Math.min( Math.max( tPixelMax + 1, iCoord), tPixelMin - 1);
          tCell = Math.floor( (iCoord - tPixelMax) * tNumCells / ( tPixelMin - tPixelMax));
        }
        else {  // horizontal
          iCoord = Math.max( Math.min( tPixelMax - tPixelMin - 1, iCoord), 1.0);
          tCell = Math.floor( iCoord * tNumCells / ( tPixelMax - tPixelMin));
        }

        //      KCP_ASSERT( (tCell >= 0) && (tCell < tNumCells));
      }
      return tCell;
    },

    /**
     Since I'm a leaf class I implement doDraw.
     */
    doDraw: function doDraw() {
      var this_ = this,
          tModel = this.get('model'),
          tBaseline = this_.get('axisLineCoordinate'),
          tOrientation = this.get('orientation'),
          tRotation = (tOrientation === 'horizontal') ? 0 : -90, // default to parallel to axis
          tMaxHeight = DG.RenderingUtilities.kDefaultFontHeight,  // So there will be a default extent
          tCentering = this.get('centering'),
          tTickOffset = tCentering ? 0 : this.get('fullCellWidth') / 2,
          tAnchor = tCentering ? 'middle' : 'start',
          tMaxWidth = tMaxHeight,
          tLabelSpecs = [],
          tCollision = false,
          tPrevLabelEnd;

      function measureOneCell( iCellNum, iCellName) {
        var tCoord = this_.cellToCoordinate( iCellNum),
            tTextElement = this_._paper.text( 0, 0, iCellName),
            tTextExtent = DG.RenderingUtilities.getExtentForTextElement(
                                tTextElement, DG.RenderingUtilities.kDefaultFontHeight);

        if( !isFinite( tCoord)) {
          tTextElement.remove();
          return;
        }
        tLabelSpecs.push( { element: tTextElement, coord: tCoord,
                            height: tTextExtent.height, width: tTextExtent.width });

        tMaxHeight = Math.max( tMaxHeight, tTextExtent.height);
        tMaxWidth = Math.max( tMaxWidth, tTextExtent.width);
        this_._elementsToClear.push( tTextElement);
        if(SC.none( tPrevLabelEnd))
          tCollision = tTextExtent.width > this_.get('fullCellWidth');
        else
          tCollision = tCollision || (tCoord - tTextExtent.width / 2 < tPrevLabelEnd);
        tPrevLabelEnd = (tCoord + tTextExtent.width / 2);
      } // measureOneCell

      // iLabelSpec has form { element: {Raphael element}, coord: {Number}, height: {Number}, width: {Number} }
      function drawOneCell( iLabelSpec) {
        var tCoord = iLabelSpec.coord,
            tLabelX, tLabelY;
        switch( this_.get('orientation')) {
          case 'vertical':
          case 'vertical2':
            this_._elementsToClear.push(
              this_._paper.line( tBaseline, tCoord + tTickOffset, tBaseline - kTickLength, tCoord + tTickOffset)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tBaseline - kTickLength - kAxisGap - iLabelSpec.height / 3;
            tLabelY = tCoord + tTickOffset;
            if( tRotation === 0) {
              tAnchor = 'end';
            }
            break;

          case 'horizontal':
            this_._elementsToClear.push(
              this_._paper.line( tCoord - tTickOffset, tBaseline, tCoord - tTickOffset, tBaseline + kTickLength)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tCoord - tTickOffset + 1;
            tLabelY = tBaseline + kTickLength + kAxisGap + iLabelSpec.height / 3;
            if( tRotation === -90) {
              tAnchor = 'end';
            }
            break;
        }
        iLabelSpec.element.attr( { x: tLabelX, y: tLabelY, 'text-anchor': tAnchor });
        DG.RenderingUtilities.rotateText( iLabelSpec.element, tRotation, tLabelX, tLabelY);
      } // drawOneCell

      //==========Main body of doDraw==================
      // Special case for short labels
      if( this.getPath('model.maxCellNameLength') <= 3)
        tRotation = 0;

      this._elementsToClear.push( this.renderAxisLine());

      tModel.forEachCellDo( measureOneCell);
      if( tCollision) // labels must be perpendicular to axis
        tRotation = (tOrientation === 'horizontal') ? -90 : 0;
      tLabelSpecs.forEach( drawOneCell);

      this.renderLabel();
      // By changing maxLabelExtent we can trigger notification that causes the graph to re-layout
      // axes and plot if needed.
      this.setIfChanged('maxLabelExtent',
            (tOrientation === 'horizontal') ?
              ((tRotation === 0) ? tMaxHeight : tMaxWidth) :  // horizontal
              ((tRotation === 0) ? tMaxWidth : tMaxHeight));  // vertical
    }

  };
}())
);

<commit_msg>[Fixes #88965868] Rather than attempt to change the frame of the x-axis (fraught with danger) to fix this clipping bug, just move the category label over 1/3 the font height for the 0th label.<commit_after>// ==========================================================================
//                            DG.CellAxisView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph/axes/axis_view');

/** @class  DG.CellAxisView - The view for a graph axis that displays cells.

 @extends DG.AxisView
 */
DG.CellAxisView = DG.AxisView.extend( (function() {
  var kTickLength = 4,
      kAxisGap = 2;

  return {
    /** @scope DG.CellAxisView.prototype */

    /**
     Number of pixels required from the axis line to the edge of the graph view
     @property { Number }
     */
    desiredExtent: function() {
      var tExtent = sc_super();
      if( !this.get('isNumeric')) { // Not yet handling numeric axis broken up by cells
        tExtent += kTickLength + kAxisGap + this.get('maxLabelExtent');
      }
      return tExtent;
    }.property('maxLabelExtent'),

    /**
     I'm not supposed to work with numbers.
     @property { Boolean }
     */
    isNumeric: false,

    /**
     * @property {Boolean}
     */
    centering: false,

    /**
     @property{Number}
     */
    axisLineCoordinate: function() {
      var tCoord;
      switch( this.get('orientation')) {
        case 'vertical':
          tCoord = this.get('drawWidth');
          break;
        case 'vertical2':
        case 'horizontal':
          tCoord = 0;
          break;
      }
      return tCoord;
    }.property('drawWidth'),

    /**
     @property {Number} Each time we draw the axis, we set this property to the maximum width
     of the label strings. Note that this will change depending not only on the length of the
     strings but on the rotation of the labels.
     */
    maxLabelExtent: 0,

    /**
     This is the main backbone of the axis.
     @return {Raphael element}
     */
    renderAxisLine: function() {
      var tCoord = this.get('axisLineCoordinate'),
          tPixelMin = this.get('pixelMin'),
          tPixelMax = this.get('pixelMax'),
          tStart, tStop;
      switch( this.get('orientation')) {
        case 'vertical':
          tStart = { x: tCoord - 1, y: tPixelMin };
          tStop = { x: tCoord - 1, y: tPixelMax };
          break;
        case 'horizontal':
          tStart = { x: tPixelMin, y: tCoord + 1 };
          tStop = { x: tPixelMax, y: tCoord + 1 };
          break;
        case 'vertical2':
          tStart = { x: tCoord + 1, y: tPixelMin };
          tStop = { x: tCoord + 1, y: tPixelMax };
          break;
      }
      return this._paper.line( tStart.x, tStart.y, tStop.x, tStop.y)
        .attr( { stroke: DG.PlotUtilities.kAxisColor,
          strokeWidth: 2 });
    },

    /**
     The convention is that, unlike continuous axes, for a
     vertical axis, the low numbers are at the top.
     @param {Number} in screen coordinates
     @return {Number} the cell number corresponding to the given screen coordinate
     */
    whichCell: function( iCoord) {
      var tNumCells = this.getPath('model.numberOfCells'),
          tCell = 0;
      if( tNumCells > 1) {
        var tPixelMin = this.get('pixelMin'),
            tPixelMax = this.get('pixelMax');
        // In order to do the computation, we have to adjust for the coordinates of the ends
        // of the axes, knowing the thePixel is in plot view coordinates, not axis view coordinates.
        if( this.get('isVertical')) {
          iCoord = Math.min( Math.max( tPixelMax + 1, iCoord), tPixelMin - 1);
          tCell = Math.floor( (iCoord - tPixelMax) * tNumCells / ( tPixelMin - tPixelMax));
        }
        else {  // horizontal
          iCoord = Math.max( Math.min( tPixelMax - tPixelMin - 1, iCoord), 1.0);
          tCell = Math.floor( iCoord * tNumCells / ( tPixelMax - tPixelMin));
        }

        //      KCP_ASSERT( (tCell >= 0) && (tCell < tNumCells));
      }
      return tCell;
    },

    /**
     Since I'm a leaf class I implement doDraw.
     */
    doDraw: function doDraw() {
      var this_ = this,
          tModel = this.get('model'),
          tBaseline = this_.get('axisLineCoordinate'),
          tOrientation = this.get('orientation'),
          tRotation = (tOrientation === 'horizontal') ? 0 : -90, // default to parallel to axis
          tMaxHeight = DG.RenderingUtilities.kDefaultFontHeight,  // So there will be a default extent
          tCentering = this.get('centering'),
          tTickOffset = tCentering ? 0 : this.get('fullCellWidth') / 2,
          tAnchor = tCentering ? 'middle' : 'start',
          tMaxWidth = tMaxHeight,
          tLabelSpecs = [],
          tCollision = false,
          tPrevLabelEnd;

      function measureOneCell( iCellNum, iCellName) {
        var tCoord = this_.cellToCoordinate( iCellNum),
            tTextElement = this_._paper.text( 0, 0, iCellName),
            tTextExtent = DG.RenderingUtilities.getExtentForTextElement(
                                tTextElement, DG.RenderingUtilities.kDefaultFontHeight);

        if( !isFinite( tCoord)) {
          tTextElement.remove();
          return;
        }
        tLabelSpecs.push( { element: tTextElement, coord: tCoord,
                            height: tTextExtent.height, width: tTextExtent.width });

        tMaxHeight = Math.max( tMaxHeight, tTextExtent.height);
        tMaxWidth = Math.max( tMaxWidth, tTextExtent.width);
        this_._elementsToClear.push( tTextElement);
        if(SC.none( tPrevLabelEnd))
          tCollision = tTextExtent.width > this_.get('fullCellWidth');
        else
          tCollision = tCollision || (tCoord - tTextExtent.width / 2 < tPrevLabelEnd);
        tPrevLabelEnd = (tCoord + tTextExtent.width / 2);
      } // measureOneCell

      // iLabelSpec has form { element: {Raphael element}, coord: {Number}, height: {Number}, width: {Number} }
      function drawOneCell( iLabelSpec, iIndex) {
        var tCoord = iLabelSpec.coord,
            tLabelX, tLabelY;
        switch( this_.get('orientation')) {
          case 'vertical':
          case 'vertical2':
            this_._elementsToClear.push(
              this_._paper.line( tBaseline, tCoord + tTickOffset, tBaseline - kTickLength, tCoord + tTickOffset)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tBaseline - kTickLength - kAxisGap - iLabelSpec.height / 3;
            tLabelY = tCoord + tTickOffset;
            if( tRotation === 0) {
              tAnchor = 'end';
            }
            break;

          case 'horizontal':
            this_._elementsToClear.push(
              this_._paper.line( tCoord - tTickOffset, tBaseline, tCoord - tTickOffset, tBaseline + kTickLength)
                .attr( { stroke: DG.PlotUtilities.kAxisColor }));
            tLabelX = tCoord - tTickOffset + 1;
            tLabelY = tBaseline + kTickLength + kAxisGap + iLabelSpec.height / 3;
            if( tRotation === -90) {
              tAnchor = 'end';
              if( iIndex === 0)
                tLabelX += iLabelSpec.height / 3;
            }
            break;
        }
        iLabelSpec.element.attr( { x: tLabelX, y: tLabelY, 'text-anchor': tAnchor });
        DG.RenderingUtilities.rotateText( iLabelSpec.element, tRotation, tLabelX, tLabelY);
      } // drawOneCell

      //==========Main body of doDraw==================
      // Special case for short labels
      if( this.getPath('model.maxCellNameLength') <= 3)
        tRotation = 0;

      this._elementsToClear.push( this.renderAxisLine());

      tModel.forEachCellDo( measureOneCell);
      if( tCollision) // labels must be perpendicular to axis
        tRotation = (tOrientation === 'horizontal') ? -90 : 0;
      tLabelSpecs.forEach( drawOneCell);

      this.renderLabel();
      // By changing maxLabelExtent we can trigger notification that causes the graph to re-layout
      // axes and plot if needed.
      this.setIfChanged('maxLabelExtent',
            (tOrientation === 'horizontal') ?
              ((tRotation === 0) ? tMaxHeight : tMaxWidth) :  // horizontal
              ((tRotation === 0) ? tMaxWidth : tMaxHeight));  // vertical
    }

  };
}())
);

<|endoftext|>