GMaps.prototype.drawPolyline = function (options) {
  var path = [],
    points = options.path;

  if (points.length) {
    if (points[0][0] === undefined) {
      path = points;
    } else {
      for (var i = 0, latlng; (latlng = points[i]); i++) {
        path.push(new google.maps.LatLng(latlng[0], latlng[1]));
      }
    }
  }

  var polyline_options = {
    map: this.map,
    path: path,
    strokeColor: options.strokeColor,
    strokeOpacity: options.strokeOpacity,
    strokeWeight: options.strokeWeight,
    geodesic: options.geodesic,
    clickable: true,
    editable: false,
    visible: true,
  };

  if (options.hasOwnProperty("clickable")) {
    polyline_options.clickable = options.clickable;
  }

  if (options.hasOwnProperty("editable")) {
    polyline_options.editable = options.editable;
  }

  if (options.hasOwnProperty("icons")) {
    polyline_options.icons = options.icons;
  }

  if (options.hasOwnProperty("zIndex")) {
    polyline_options.zIndex = options.zIndex;
  }

  var polyline = new google.maps.Polyline(polyline_options);

  var polyline_events = [
    "click",
    "dblclick",
    "mousedown",
    "mousemove",
    "mouseout",
    "mouseover",
    "mouseup",
    "rightclick",
  ];

  for (var ev = 0; ev < polyline_events.length; ev++) {
    (function (object, name) {
      if (options[name]) {
        google.maps.event.addListener(object, name, function (e) {
          options[name].apply(this, [e]);
        });
      }
    })(polyline, polyline_events[ev]);
  }

  this.polylines.push(polyline);

  GMaps.fire("polyline_added", polyline, this);

  return polyline;
};

GMaps.prototype.removePolyline = function (polyline) {
  for (var i = 0; i < this.polylines.length; i++) {
    if (this.polylines[i] === polyline) {
      this.polylines[i].setMap(null);
      this.polylines.splice(i, 1);

      GMaps.fire("polyline_removed", polyline, this);

      break;
    }
  }
};

GMaps.prototype.removePolylines = function () {
  for (var i = 0, item; (item = this.polylines[i]); i++) {
    item.setMap(null);
  }

  this.polylines = [];
};

GMaps.prototype.drawCircle = function (options) {
  options = extend_object(
    {
      map: this.map,
      center: new google.maps.LatLng(options.lat, options.lng),
    },
    options
  );

  delete options.lat;
  delete options.lng;

  var polygon = new google.maps.Circle(options),
    polygon_events = [
      "click",
      "dblclick",
      "mousedown",
      "mousemove",
      "mouseout",
      "mouseover",
      "mouseup",
      "rightclick",
    ];

  for (var ev = 0; ev < polygon_events.length; ev++) {
    (function (object, name) {
      if (options[name]) {
        google.maps.event.addListener(object, name, function (e) {
          options[name].apply(this, [e]);
        });
      }
    })(polygon, polygon_events[ev]);
  }

  this.polygons.push(polygon);

  return polygon;
};

GMaps.prototype.drawRectangle = function (options) {
  options = extend_object(
    {
      map: this.map,
    },
    options
  );

  var latLngBounds = new google.maps.LatLngBounds(
    new google.maps.LatLng(options.bounds[0][0], options.bounds[0][1]),
    new google.maps.LatLng(options.bounds[1][0], options.bounds[1][1])
  );

  options.bounds = latLngBounds;

  var polygon = new google.maps.Rectangle(options),
    polygon_events = [
      "click",
      "dblclick",
      "mousedown",
      "mousemove",
      "mouseout",
      "mouseover",
      "mouseup",
      "rightclick",
    ];

  for (var ev = 0; ev < polygon_events.length; ev++) {
    (function (object, name) {
      if (options[name]) {
        google.maps.event.addListener(object, name, function (e) {
          options[name].apply(this, [e]);
        });
      }
    })(polygon, polygon_events[ev]);
  }

  this.polygons.push(polygon);

  return polygon;
};

GMaps.prototype.drawPolygon = function (options) {
  var useGeoJSON = false;

  if (options.hasOwnProperty("useGeoJSON")) {
    useGeoJSON = options.useGeoJSON;
  }

  delete options.useGeoJSON;

  options = extend_object(
    {
      map: this.map,
    },
    options
  );

  if (useGeoJSON == false) {
    options.paths = [options.paths.slice(0)];
  }

  if (options.paths.length > 0) {
    if (options.paths[0].length > 0) {
      options.paths = array_flat(
        array_map(options.paths, arrayToLatLng, useGeoJSON)
      );
    }
  }

  var polygon = new google.maps.Polygon(options),
    polygon_events = [
      "click",
      "dblclick",
      "mousedown",
      "mousemove",
      "mouseout",
      "mouseover",
      "mouseup",
      "rightclick",
    ];

  for (var ev = 0; ev < polygon_events.length; ev++) {
    (function (object, name) {
      if (options[name]) {
        google.maps.event.addListener(object, name, function (e) {
          options[name].apply(this, [e]);
        });
      }
    })(polygon, polygon_events[ev]);
  }

  this.polygons.push(polygon);

  GMaps.fire("polygon_added", polygon, this);

  return polygon;
};

GMaps.prototype.removePolygon = function (polygon) {
  for (var i = 0; i < this.polygons.length; i++) {
    if (this.polygons[i] === polygon) {
      this.polygons[i].setMap(null);
      this.polygons.splice(i, 1);

      GMaps.fire("polygon_removed", polygon, this);

      break;
    }
  }
};

GMaps.prototype.removePolygons = function () {
  for (var i = 0, item; (item = this.polygons[i]); i++) {
    item.setMap(null);
  }

  this.polygons = [];
};
