po.arrow = function() {
  var arrow = {},
      key = {left: 0, right: 0, up: 0, down: 0},
      last = 0,
      repeatTimer,
      repeatDelay = 250,
      repeatInterval = 50,
      speed = 16,
      map,
      parent;

  function keydown(e) {
    if (e.ctrlKey || e.altKey || e.metaKey) return;
    var now = Date.now(), dx = 0, dy = 0;
    switch (e.keyCode) {
      case 37: {
        if (!key.left) {
          last = now;
          key.left = 1;
          if (!key.right) dx = speed;
        }
        break;
      }
      case 39: {
        if (!key.right) {
          last = now;
          key.right = 1;
          if (!key.left) dx = -speed;
        }
        break;
      }
      case 38: {
        if (!key.up) {
          last = now;
          key.up = 1;
          if (!key.down) dy = speed;
        }
        break;
      }
      case 40: {
        if (!key.down) {
          last = now;
          key.down = 1;
          if (!key.up) dy = -speed;
        }
        break;
      }
      default: return;
    }
    if (dx || dy) map.panBy({x: dx, y: dy});
    if (!repeatTimer && (key.left | key.right | key.up | key.down)) {
      repeatTimer = setInterval(repeat, repeatInterval);
    }
    e.preventDefault();
  }

  function keyup(e) {
    last = Date.now();
    switch (e.keyCode) {
      case 37: key.left = 0; break;
      case 39: key.right = 0; break;
      case 38: key.up = 0; break;
      case 40: key.down = 0; break;
      default: return;
    }
    if (repeatTimer && !(key.left | key.right | key.up | key.down)) {
      repeatTimer = clearInterval(repeatTimer);
    }
    e.preventDefault();
  }

  function keypress(e) {
    switch (e.charCode) {
      case 45: case 95: map.zoom(Math.ceil(map.zoom()) - 1); break; // - _
      case 43: case 61: map.zoom(Math.floor(map.zoom()) + 1); break; // = +
      default: return;
    }
    e.preventDefault();
  }

  function repeat() {
    if (!map) return;
    if (Date.now() < last + repeatDelay) return;
    var dx = (key.left - key.right) * speed,
        dy = (key.up - key.down) * speed;
    if (dx || dy) map.panBy({x: dx, y: dy});
  }

  arrow.map = function(x) {
    if (!arguments.length) return map;
    if (map) {
      parent.removeEventListener("keypress", keypress, false);
      parent.removeEventListener("keydown", keydown, false);
      parent.removeEventListener("keyup", keyup, false);
      parent = null;
    }
    if (map = x) {
      parent = map.focusableParent();
      parent.addEventListener("keypress", keypress, false);
      parent.addEventListener("keydown", keydown, false);
      parent.addEventListener("keyup", keyup, false);
    }
    return arrow;
  };

  arrow.speed = function(x) {
    if (!arguments.length) return speed;
    speed = x;
    return arrow;
  };

  return arrow;
};
