docs: function(target, loc) {
          var resolution = this.layer.map.getResolution();
          if ("minResolution" in target) {
              if (resolution < target.minResolution) {
                  return null;
              }
          }
          if ("maxResolution" in target) {
              if (resolution >= target.maxResolution) {
                  return null;
              }
          }
          var tolerance = {
              node: this.getGeoTolerance(target.nodeTolerance, resolution),
              vertex: this.getGeoTolerance(target.vertexTolerance, resolution),
              edge: this.getGeoTolerance(target.edgeTolerance, resolution)
          };
          // this could be cached if we don't support setting tolerance values directly
          var maxTolerance = Math.max(
              tolerance.node, tolerance.vertex, tolerance.edge
          );
          var result = {
              rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY
          };
          var eligible = false;
          var features = target.layer.features;
          var feature, type, vertices, vertex, closest, dist, found;
          var numTypes = this.precedence.length;
          var ll = new OpenLayers.LonLat(loc.x, loc.y);
          for(var i=0, len=features.length; i<len; ++i) {
              feature = features[i];
              if(feature !== this.feature && !feature._sketch &&
                 feature.state !== OpenLayers.State.DELETE &&
                 (!target.filter || target.filter.evaluate(feature))) {
                  if(feature.atPoint(ll, maxTolerance, maxTolerance)) {
                      for(var j=0, stop=Math.min(result.rank+1, numTypes); j<stop; ++j) {
                          type = this.precedence[j];
                          if(target[type]) {
                              if(type === "edge") {
                                  closest = feature.geometry.distanceTo(loc, {details: true});
                                  dist = closest.distance;
                                  if(dist <= tolerance[type] && dist < result.dist) {
                                      result = {
                                          rank: j, dist: dist,
                                          x: closest.x0, y: closest.y0 // closest coords on feature
                                      };
                                      eligible = true;
                                      // don't look for lower precedence types for this feature
                                      break;
                                  }
                              } else {
                                  // look for nodes or vertices
                                  vertices = feature.geometry.getVertices(type === "node");
                                  found = false;
                                  for(var k=0, klen=vertices.length; k<klen; ++k) {
                                      vertex = vertices[k];
                                      dist = vertex.distanceTo(loc);
                                      if(dist <= tolerance[type] &&
                                         (j < result.rank || (j === result.rank && dist < result.dist))) {
                                          result = {
                                              rank: j, dist: dist,
                                              x: vertex.x, y: vertex.y
                                          };
                                          eligible = true;
                                          found = true;
                                      }
                                  }
                                  if(found) {
                                      // don't look for lower precedence types for this feature
                                      break;
                                  }
                              }
                          }
                      }
                  }
              }
          }
          return eligible ? result : null;
      }